]> sourceware.org Git - dwz.git/commitdiff
dwz-0.1 - first public release dwz-0.1
authorJakub Jelinek <jakub@redhat.com>
Thu, 19 Apr 2012 11:32:50 +0000 (13:32 +0200)
committerJakub Jelinek <jakub@redhat.com>
Thu, 19 Apr 2012 11:32:50 +0000 (13:32 +0200)
Makefile [new file with mode: 0644]
dwarf2.h [new file with mode: 0644]
dwz.c [new file with mode: 0644]
hashtab.c [new file with mode: 0644]
hashtab.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..5458ed3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+CFLAGS = -O2 -g -Wall -W
+OBJECTS = dwz.o hashtab.o
+dwz: $(OBJECTS)
+       $(CC) $(LDFLAGS) -o $@ $^ -lelf
+clean:
+       rm -f $(OBJECTS) *~ core* dwz
diff --git a/dwarf2.h b/dwarf2.h
new file mode 100644 (file)
index 0000000..8c0c9ed
--- /dev/null
+++ b/dwarf2.h
@@ -0,0 +1,933 @@
+/* Declarations and definitions of codes relating to the DWARF2 and
+   DWARF3 symbolic debugging information formats.
+   Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002,
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   Free Software Foundation, Inc.
+
+   Written by Gary Funck (gary@intrepid.com) The Ada Joint Program
+   Office (AJPO), Florida State University and Silicon Graphics Inc.
+   provided support for this effort -- June 21, 1995.
+
+   Derived from the DWARF 1 implementation written by Ron Guilmette
+   (rfg@netcom.com), November 1990.
+
+   This file is part of GCC.
+
+   GCC 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, or (at your option) any later
+   version.
+
+   GCC 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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This file is derived from the DWARF specification (a public document)
+   Revision 2.0.0 (July 27, 1993) developed by the UNIX International
+   Programming Languages Special Interest Group (UI/PLSIG) and distributed
+   by UNIX International.  Copies of this specification are available from
+   UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054.
+
+   This file also now contains definitions from the DWARF 3 specification
+   published Dec 20, 2005, available from: http://dwarf.freestandards.org.  */
+
+/* This file is shared between GCC and GDB, and should not contain
+   prototypes.  */
+
+#ifndef _DWARF2_H
+#define _DWARF2_H
+
+/* Tag names and codes.  */
+enum dwarf_tag
+  {
+    DW_TAG_padding = 0x00,
+    DW_TAG_array_type = 0x01,
+    DW_TAG_class_type = 0x02,
+    DW_TAG_entry_point = 0x03,
+    DW_TAG_enumeration_type = 0x04,
+    DW_TAG_formal_parameter = 0x05,
+    DW_TAG_imported_declaration = 0x08,
+    DW_TAG_label = 0x0a,
+    DW_TAG_lexical_block = 0x0b,
+    DW_TAG_member = 0x0d,
+    DW_TAG_pointer_type = 0x0f,
+    DW_TAG_reference_type = 0x10,
+    DW_TAG_compile_unit = 0x11,
+    DW_TAG_string_type = 0x12,
+    DW_TAG_structure_type = 0x13,
+    DW_TAG_subroutine_type = 0x15,
+    DW_TAG_typedef = 0x16,
+    DW_TAG_union_type = 0x17,
+    DW_TAG_unspecified_parameters = 0x18,
+    DW_TAG_variant = 0x19,
+    DW_TAG_common_block = 0x1a,
+    DW_TAG_common_inclusion = 0x1b,
+    DW_TAG_inheritance = 0x1c,
+    DW_TAG_inlined_subroutine = 0x1d,
+    DW_TAG_module = 0x1e,
+    DW_TAG_ptr_to_member_type = 0x1f,
+    DW_TAG_set_type = 0x20,
+    DW_TAG_subrange_type = 0x21,
+    DW_TAG_with_stmt = 0x22,
+    DW_TAG_access_declaration = 0x23,
+    DW_TAG_base_type = 0x24,
+    DW_TAG_catch_block = 0x25,
+    DW_TAG_const_type = 0x26,
+    DW_TAG_constant = 0x27,
+    DW_TAG_enumerator = 0x28,
+    DW_TAG_file_type = 0x29,
+    DW_TAG_friend = 0x2a,
+    DW_TAG_namelist = 0x2b,
+    DW_TAG_namelist_item = 0x2c,
+    DW_TAG_packed_type = 0x2d,
+    DW_TAG_subprogram = 0x2e,
+    DW_TAG_template_type_param = 0x2f,
+    DW_TAG_template_value_param = 0x30,
+    DW_TAG_thrown_type = 0x31,
+    DW_TAG_try_block = 0x32,
+    DW_TAG_variant_part = 0x33,
+    DW_TAG_variable = 0x34,
+    DW_TAG_volatile_type = 0x35,
+    /* DWARF 3.  */
+    DW_TAG_dwarf_procedure = 0x36,
+    DW_TAG_restrict_type = 0x37,
+    DW_TAG_interface_type = 0x38,
+    DW_TAG_namespace = 0x39,
+    DW_TAG_imported_module = 0x3a,
+    DW_TAG_unspecified_type = 0x3b,
+    DW_TAG_partial_unit = 0x3c,
+    DW_TAG_imported_unit = 0x3d,
+    DW_TAG_condition = 0x3f,
+    DW_TAG_shared_type = 0x40,
+    /* DWARF 4.  */
+    DW_TAG_type_unit = 0x41,
+    DW_TAG_rvalue_reference_type = 0x42,
+    DW_TAG_template_alias = 0x43,
+
+    DW_TAG_lo_user = 0x4080,
+    DW_TAG_hi_user = 0xffff,
+
+    /* SGI/MIPS Extensions.  */
+    DW_TAG_MIPS_loop = 0x4081,
+
+    /* HP extensions.  See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .  */
+    DW_TAG_HP_array_descriptor = 0x4090,
+    DW_TAG_HP_Bliss_field      = 0x4091,
+    DW_TAG_HP_Bliss_field_set  = 0x4092,
+
+    /* GNU extensions.  */
+    DW_TAG_format_label = 0x4101,      /* For FORTRAN 77 and Fortran 90.  */
+    DW_TAG_function_template = 0x4102, /* For C++.  */
+    DW_TAG_class_template = 0x4103,    /* For C++.  */
+    DW_TAG_GNU_BINCL = 0x4104,
+    DW_TAG_GNU_EINCL = 0x4105,
+    /* Template template parameter.
+       See http://gcc.gnu.org/wiki/TemplateParmsDwarf .  */
+    DW_TAG_GNU_template_template_param = 0x4106,
+
+    /* Template parameter pack extension, specified at
+       http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates
+       The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
+       are properly part of DWARF 5.  */
+    DW_TAG_GNU_template_parameter_pack = 0x4107,
+    DW_TAG_GNU_formal_parameter_pack = 0x4108,
+    /* The GNU call site extension, specified at
+       http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
+       The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
+       are properly part of DWARF 5.  */
+    DW_TAG_GNU_call_site = 0x4109,
+    DW_TAG_GNU_call_site_parameter = 0x410a,
+    /* Extensions for UPC.  See: http://upc.gwu.edu/~upc.  */
+    DW_TAG_upc_shared_type = 0x8765,
+    DW_TAG_upc_strict_type = 0x8766,
+    DW_TAG_upc_relaxed_type = 0x8767,
+    /* PGI (STMicroelectronics) extensions.  No documentation available.  */
+    DW_TAG_PGI_kanji_type      = 0xA000,
+    DW_TAG_PGI_interface_block = 0xA020
+  };
+
+/* Flag that tells whether entry has a child or not.  */
+#define DW_children_no   0
+#define        DW_children_yes  1
+
+/* Form names and codes.  */
+enum dwarf_form
+  {
+    DW_FORM_addr = 0x01,
+    DW_FORM_block2 = 0x03,
+    DW_FORM_block4 = 0x04,
+    DW_FORM_data2 = 0x05,
+    DW_FORM_data4 = 0x06,
+    DW_FORM_data8 = 0x07,
+    DW_FORM_string = 0x08,
+    DW_FORM_block = 0x09,
+    DW_FORM_block1 = 0x0a,
+    DW_FORM_data1 = 0x0b,
+    DW_FORM_flag = 0x0c,
+    DW_FORM_sdata = 0x0d,
+    DW_FORM_strp = 0x0e,
+    DW_FORM_udata = 0x0f,
+    DW_FORM_ref_addr = 0x10,
+    DW_FORM_ref1 = 0x11,
+    DW_FORM_ref2 = 0x12,
+    DW_FORM_ref4 = 0x13,
+    DW_FORM_ref8 = 0x14,
+    DW_FORM_ref_udata = 0x15,
+    DW_FORM_indirect = 0x16,
+    /* DWARF 4.  */
+    DW_FORM_sec_offset = 0x17,
+    DW_FORM_exprloc = 0x18,
+    DW_FORM_flag_present = 0x19,
+    DW_FORM_ref_sig8 = 0x20,
+    /* Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
+    DW_FORM_GNU_ref_index = 0x1f00,
+    DW_FORM_GNU_addr_index = 0x1f01,
+    DW_FORM_GNU_str_index = 0x1f02
+  };
+
+/* Attribute names and codes.  */
+enum dwarf_attribute
+  {
+    DW_AT_sibling = 0x01,
+    DW_AT_location = 0x02,
+    DW_AT_name = 0x03,
+    DW_AT_ordering = 0x09,
+    DW_AT_subscr_data = 0x0a,
+    DW_AT_byte_size = 0x0b,
+    DW_AT_bit_offset = 0x0c,
+    DW_AT_bit_size = 0x0d,
+    DW_AT_element_list = 0x0f,
+    DW_AT_stmt_list = 0x10,
+    DW_AT_low_pc = 0x11,
+    DW_AT_high_pc = 0x12,
+    DW_AT_language = 0x13,
+    DW_AT_member = 0x14,
+    DW_AT_discr = 0x15,
+    DW_AT_discr_value = 0x16,
+    DW_AT_visibility = 0x17,
+    DW_AT_import = 0x18,
+    DW_AT_string_length = 0x19,
+    DW_AT_common_reference = 0x1a,
+    DW_AT_comp_dir = 0x1b,
+    DW_AT_const_value = 0x1c,
+    DW_AT_containing_type = 0x1d,
+    DW_AT_default_value = 0x1e,
+    DW_AT_inline = 0x20,
+    DW_AT_is_optional = 0x21,
+    DW_AT_lower_bound = 0x22,
+    DW_AT_producer = 0x25,
+    DW_AT_prototyped = 0x27,
+    DW_AT_return_addr = 0x2a,
+    DW_AT_start_scope = 0x2c,
+    DW_AT_bit_stride = 0x2e,
+#define DW_AT_stride_size   DW_AT_bit_stride  /* Note: The use of DW_AT_stride_size is deprecated.  */
+    DW_AT_upper_bound = 0x2f,
+    DW_AT_abstract_origin = 0x31,
+    DW_AT_accessibility = 0x32,
+    DW_AT_address_class = 0x33,
+    DW_AT_artificial = 0x34,
+    DW_AT_base_types = 0x35,
+    DW_AT_calling_convention = 0x36,
+    DW_AT_count = 0x37,
+    DW_AT_data_member_location = 0x38,
+    DW_AT_decl_column = 0x39,
+    DW_AT_decl_file = 0x3a,
+    DW_AT_decl_line = 0x3b,
+    DW_AT_declaration = 0x3c,
+    DW_AT_discr_list = 0x3d,
+    DW_AT_encoding = 0x3e,
+    DW_AT_external = 0x3f,
+    DW_AT_frame_base = 0x40,
+    DW_AT_friend = 0x41,
+    DW_AT_identifier_case = 0x42,
+    DW_AT_macro_info = 0x43,
+    DW_AT_namelist_items = 0x44,
+    DW_AT_priority = 0x45,
+    DW_AT_segment = 0x46,
+    DW_AT_specification = 0x47,
+    DW_AT_static_link = 0x48,
+    DW_AT_type = 0x49,
+    DW_AT_use_location = 0x4a,
+    DW_AT_variable_parameter = 0x4b,
+    DW_AT_virtuality = 0x4c,
+    DW_AT_vtable_elem_location = 0x4d,
+    /* DWARF 3 values.  */
+    DW_AT_allocated     = 0x4e,
+    DW_AT_associated    = 0x4f,
+    DW_AT_data_location = 0x50,
+    DW_AT_byte_stride   = 0x51,
+#define DW_AT_stride   DW_AT_byte_stride  /* Note: The use of DW_AT_stride is deprecated.  */
+    DW_AT_entry_pc      = 0x52,
+    DW_AT_use_UTF8      = 0x53,
+    DW_AT_extension     = 0x54,
+    DW_AT_ranges        = 0x55,
+    DW_AT_trampoline    = 0x56,
+    DW_AT_call_column   = 0x57,
+    DW_AT_call_file     = 0x58,
+    DW_AT_call_line     = 0x59,
+    DW_AT_description   = 0x5a,
+    DW_AT_binary_scale  = 0x5b,
+    DW_AT_decimal_scale = 0x5c,
+    DW_AT_small         = 0x5d,
+    DW_AT_decimal_sign  = 0x5e,
+    DW_AT_digit_count   = 0x5f,
+    DW_AT_picture_string = 0x60,
+    DW_AT_mutable       = 0x61,
+    DW_AT_threads_scaled = 0x62,
+    DW_AT_explicit      = 0x63,
+    DW_AT_object_pointer = 0x64,
+    DW_AT_endianity     = 0x65,
+    DW_AT_elemental     = 0x66,
+    DW_AT_pure          = 0x67,
+    DW_AT_recursive     = 0x68,
+    /* DWARF 4.  */
+    DW_AT_signature       = 0x69,
+    DW_AT_main_subprogram = 0x6a,
+    DW_AT_data_bit_offset = 0x6b,
+    DW_AT_const_expr      = 0x6c,
+    DW_AT_enum_class      = 0x6d,
+    DW_AT_linkage_name    = 0x6e,
+
+    DW_AT_lo_user = 0x2000,    /* Implementation-defined range start.  */
+    DW_AT_hi_user = 0x3fff,    /* Implementation-defined range end.  */
+
+    /* SGI/MIPS extensions.  */
+    DW_AT_MIPS_fde = 0x2001,
+    DW_AT_MIPS_loop_begin = 0x2002,
+    DW_AT_MIPS_tail_loop_begin = 0x2003,
+    DW_AT_MIPS_epilog_begin = 0x2004,
+    DW_AT_MIPS_loop_unroll_factor = 0x2005,
+    DW_AT_MIPS_software_pipeline_depth = 0x2006,
+    DW_AT_MIPS_linkage_name = 0x2007,
+    DW_AT_MIPS_stride = 0x2008,
+    DW_AT_MIPS_abstract_name = 0x2009,
+    DW_AT_MIPS_clone_origin = 0x200a,
+    DW_AT_MIPS_has_inlines = 0x200b,
+    /* HP extensions.  */
+    DW_AT_HP_block_index         = 0x2000,
+    DW_AT_HP_unmodifiable        = 0x2001, /* Same as DW_AT_MIPS_fde.  */
+    DW_AT_HP_prologue            = 0x2005, /* Same as DW_AT_MIPS_loop_unroll.  */
+    DW_AT_HP_epilogue            = 0x2008, /* Same as DW_AT_MIPS_stride.  */
+    DW_AT_HP_actuals_stmt_list   = 0x2010,
+    DW_AT_HP_proc_per_section    = 0x2011,
+    DW_AT_HP_raw_data_ptr        = 0x2012,
+    DW_AT_HP_pass_by_reference   = 0x2013,
+    DW_AT_HP_opt_level           = 0x2014,
+    DW_AT_HP_prof_version_id     = 0x2015,
+    DW_AT_HP_opt_flags           = 0x2016,
+    DW_AT_HP_cold_region_low_pc  = 0x2017,
+    DW_AT_HP_cold_region_high_pc = 0x2018,
+    DW_AT_HP_all_variables_modifiable = 0x2019,
+    DW_AT_HP_linkage_name        = 0x201a,
+    DW_AT_HP_prof_flags          = 0x201b,  /* In comp unit of procs_info for -g.  */
+    DW_AT_HP_unit_name           = 0x201f,
+    DW_AT_HP_unit_size           = 0x2020,
+    DW_AT_HP_widened_byte_size   = 0x2021,
+    DW_AT_HP_definition_points   = 0x2022,
+    DW_AT_HP_default_location    = 0x2023,
+    DW_AT_HP_is_result_param     = 0x2029,
+
+    /* GNU extensions.  */
+    DW_AT_sf_names   = 0x2101,
+    DW_AT_src_info   = 0x2102,
+    DW_AT_mac_info   = 0x2103,
+    DW_AT_src_coords = 0x2104,
+    DW_AT_body_begin = 0x2105,
+    DW_AT_body_end   = 0x2106,
+    DW_AT_GNU_vector = 0x2107,
+    /* Thread-safety annotations.
+       See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation .  */
+    DW_AT_GNU_guarded_by    = 0x2108,
+    DW_AT_GNU_pt_guarded_by = 0x2109,
+    DW_AT_GNU_guarded       = 0x210a,
+    DW_AT_GNU_pt_guarded    = 0x210b,
+    DW_AT_GNU_locks_excluded = 0x210c,
+    DW_AT_GNU_exclusive_locks_required = 0x210d,
+    DW_AT_GNU_shared_locks_required = 0x210e,
+    /* One-definition rule violation detection.
+       See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo .  */
+    DW_AT_GNU_odr_signature = 0x210f,
+    /* Template template argument name.
+       See http://gcc.gnu.org/wiki/TemplateParmsDwarf .  */
+    DW_AT_GNU_template_name = 0x2110,
+    /* The GNU call site extension.
+       See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .  */
+    DW_AT_GNU_call_site_value = 0x2111,
+    DW_AT_GNU_call_site_data_value = 0x2112,
+    DW_AT_GNU_call_site_target = 0x2113,
+    DW_AT_GNU_call_site_target_clobbered = 0x2114,
+    DW_AT_GNU_tail_call = 0x2115,
+    DW_AT_GNU_all_tail_call_sites = 0x2116,
+    DW_AT_GNU_all_call_sites = 0x2117,
+    DW_AT_GNU_all_source_call_sites = 0x2118,
+    /* Section offset into .debug_macro section.  */
+    DW_AT_GNU_macros = 0x2119,
+    /* Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
+    DW_AT_GNU_dwo_name = 0x2130,
+    DW_AT_GNU_dwo_id = 0x2131,
+    DW_AT_GNU_ref_base = 0x2132,
+    DW_AT_GNU_addr_base = 0x2133,
+    DW_AT_GNU_pubnames = 0x2134,
+    DW_AT_GNU_pubtypes = 0x2135,
+    /* VMS extensions.  */
+    DW_AT_VMS_rtnbeg_pd_address = 0x2201,
+    /* GNAT extensions.  */
+    /* GNAT descriptive type.
+       See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type .  */
+    DW_AT_use_GNAT_descriptive_type = 0x2301,
+    DW_AT_GNAT_descriptive_type        = 0x2302,
+    /* UPC extension.  */
+    DW_AT_upc_threads_scaled = 0x3210,
+    /* PGI (STMicroelectronics) extensions.  */
+    DW_AT_PGI_lbase    = 0x3a00,
+    DW_AT_PGI_soffset  = 0x3a01,
+    DW_AT_PGI_lstride  = 0x3a02
+  };
+
+/* Location atom names and codes.  */
+enum dwarf_location_atom
+  {
+    DW_OP_addr = 0x03,
+    DW_OP_deref = 0x06,
+    DW_OP_const1u = 0x08,
+    DW_OP_const1s = 0x09,
+    DW_OP_const2u = 0x0a,
+    DW_OP_const2s = 0x0b,
+    DW_OP_const4u = 0x0c,
+    DW_OP_const4s = 0x0d,
+    DW_OP_const8u = 0x0e,
+    DW_OP_const8s = 0x0f,
+    DW_OP_constu = 0x10,
+    DW_OP_consts = 0x11,
+    DW_OP_dup = 0x12,
+    DW_OP_drop = 0x13,
+    DW_OP_over = 0x14,
+    DW_OP_pick = 0x15,
+    DW_OP_swap = 0x16,
+    DW_OP_rot = 0x17,
+    DW_OP_xderef = 0x18,
+    DW_OP_abs = 0x19,
+    DW_OP_and = 0x1a,
+    DW_OP_div = 0x1b,
+    DW_OP_minus = 0x1c,
+    DW_OP_mod = 0x1d,
+    DW_OP_mul = 0x1e,
+    DW_OP_neg = 0x1f,
+    DW_OP_not = 0x20,
+    DW_OP_or = 0x21,
+    DW_OP_plus = 0x22,
+    DW_OP_plus_uconst = 0x23,
+    DW_OP_shl = 0x24,
+    DW_OP_shr = 0x25,
+    DW_OP_shra = 0x26,
+    DW_OP_xor = 0x27,
+    DW_OP_bra = 0x28,
+    DW_OP_eq = 0x29,
+    DW_OP_ge = 0x2a,
+    DW_OP_gt = 0x2b,
+    DW_OP_le = 0x2c,
+    DW_OP_lt = 0x2d,
+    DW_OP_ne = 0x2e,
+    DW_OP_skip = 0x2f,
+    DW_OP_lit0 = 0x30,
+    DW_OP_lit1 = 0x31,
+    DW_OP_lit2 = 0x32,
+    DW_OP_lit3 = 0x33,
+    DW_OP_lit4 = 0x34,
+    DW_OP_lit5 = 0x35,
+    DW_OP_lit6 = 0x36,
+    DW_OP_lit7 = 0x37,
+    DW_OP_lit8 = 0x38,
+    DW_OP_lit9 = 0x39,
+    DW_OP_lit10 = 0x3a,
+    DW_OP_lit11 = 0x3b,
+    DW_OP_lit12 = 0x3c,
+    DW_OP_lit13 = 0x3d,
+    DW_OP_lit14 = 0x3e,
+    DW_OP_lit15 = 0x3f,
+    DW_OP_lit16 = 0x40,
+    DW_OP_lit17 = 0x41,
+    DW_OP_lit18 = 0x42,
+    DW_OP_lit19 = 0x43,
+    DW_OP_lit20 = 0x44,
+    DW_OP_lit21 = 0x45,
+    DW_OP_lit22 = 0x46,
+    DW_OP_lit23 = 0x47,
+    DW_OP_lit24 = 0x48,
+    DW_OP_lit25 = 0x49,
+    DW_OP_lit26 = 0x4a,
+    DW_OP_lit27 = 0x4b,
+    DW_OP_lit28 = 0x4c,
+    DW_OP_lit29 = 0x4d,
+    DW_OP_lit30 = 0x4e,
+    DW_OP_lit31 = 0x4f,
+    DW_OP_reg0 = 0x50,
+    DW_OP_reg1 = 0x51,
+    DW_OP_reg2 = 0x52,
+    DW_OP_reg3 = 0x53,
+    DW_OP_reg4 = 0x54,
+    DW_OP_reg5 = 0x55,
+    DW_OP_reg6 = 0x56,
+    DW_OP_reg7 = 0x57,
+    DW_OP_reg8 = 0x58,
+    DW_OP_reg9 = 0x59,
+    DW_OP_reg10 = 0x5a,
+    DW_OP_reg11 = 0x5b,
+    DW_OP_reg12 = 0x5c,
+    DW_OP_reg13 = 0x5d,
+    DW_OP_reg14 = 0x5e,
+    DW_OP_reg15 = 0x5f,
+    DW_OP_reg16 = 0x60,
+    DW_OP_reg17 = 0x61,
+    DW_OP_reg18 = 0x62,
+    DW_OP_reg19 = 0x63,
+    DW_OP_reg20 = 0x64,
+    DW_OP_reg21 = 0x65,
+    DW_OP_reg22 = 0x66,
+    DW_OP_reg23 = 0x67,
+    DW_OP_reg24 = 0x68,
+    DW_OP_reg25 = 0x69,
+    DW_OP_reg26 = 0x6a,
+    DW_OP_reg27 = 0x6b,
+    DW_OP_reg28 = 0x6c,
+    DW_OP_reg29 = 0x6d,
+    DW_OP_reg30 = 0x6e,
+    DW_OP_reg31 = 0x6f,
+    DW_OP_breg0 = 0x70,
+    DW_OP_breg1 = 0x71,
+    DW_OP_breg2 = 0x72,
+    DW_OP_breg3 = 0x73,
+    DW_OP_breg4 = 0x74,
+    DW_OP_breg5 = 0x75,
+    DW_OP_breg6 = 0x76,
+    DW_OP_breg7 = 0x77,
+    DW_OP_breg8 = 0x78,
+    DW_OP_breg9 = 0x79,
+    DW_OP_breg10 = 0x7a,
+    DW_OP_breg11 = 0x7b,
+    DW_OP_breg12 = 0x7c,
+    DW_OP_breg13 = 0x7d,
+    DW_OP_breg14 = 0x7e,
+    DW_OP_breg15 = 0x7f,
+    DW_OP_breg16 = 0x80,
+    DW_OP_breg17 = 0x81,
+    DW_OP_breg18 = 0x82,
+    DW_OP_breg19 = 0x83,
+    DW_OP_breg20 = 0x84,
+    DW_OP_breg21 = 0x85,
+    DW_OP_breg22 = 0x86,
+    DW_OP_breg23 = 0x87,
+    DW_OP_breg24 = 0x88,
+    DW_OP_breg25 = 0x89,
+    DW_OP_breg26 = 0x8a,
+    DW_OP_breg27 = 0x8b,
+    DW_OP_breg28 = 0x8c,
+    DW_OP_breg29 = 0x8d,
+    DW_OP_breg30 = 0x8e,
+    DW_OP_breg31 = 0x8f,
+    DW_OP_regx = 0x90,
+    DW_OP_fbreg = 0x91,
+    DW_OP_bregx = 0x92,
+    DW_OP_piece = 0x93,
+    DW_OP_deref_size = 0x94,
+    DW_OP_xderef_size = 0x95,
+    DW_OP_nop = 0x96,
+    /* DWARF 3 extensions.  */
+    DW_OP_push_object_address = 0x97,
+    DW_OP_call2 = 0x98,
+    DW_OP_call4 = 0x99,
+    DW_OP_call_ref = 0x9a,
+    DW_OP_form_tls_address = 0x9b,
+    DW_OP_call_frame_cfa = 0x9c,
+    DW_OP_bit_piece = 0x9d,
+
+    /* DWARF 4 extensions.  */
+    DW_OP_implicit_value = 0x9e,
+    DW_OP_stack_value = 0x9f,
+
+    DW_OP_lo_user = 0xe0,      /* Implementation-defined range start.  */
+    DW_OP_hi_user = 0xff,      /* Implementation-defined range end.  */
+
+    /* GNU extensions.  */
+    DW_OP_GNU_push_tls_address = 0xe0,
+    /* The following is for marking variables that are uninitialized.  */
+    DW_OP_GNU_uninit     = 0xf0,
+    DW_OP_GNU_encoded_addr = 0xf1,
+    /* The GNU implicit pointer extension.
+       See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open .  */
+    DW_OP_GNU_implicit_pointer = 0xf2,
+    /* The GNU entry value extension.
+       See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open .  */
+    DW_OP_GNU_entry_value = 0xf3,
+    /* The GNU typed stack extension.
+       See http://www.dwarfstd.org/doc/040408.1.html .  */
+    DW_OP_GNU_const_type = 0xf4,
+    DW_OP_GNU_regval_type = 0xf5,
+    DW_OP_GNU_deref_type = 0xf6,
+    DW_OP_GNU_convert = 0xf7,
+    DW_OP_GNU_reinterpret = 0xf9,
+    /* The GNU parameter ref extension.  */
+    DW_OP_GNU_parameter_ref = 0xfa,
+    /* HP extensions.  */
+    DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
+    DW_OP_HP_is_value    = 0xe1,
+    DW_OP_HP_fltconst4   = 0xe2,
+    DW_OP_HP_fltconst8   = 0xe3,
+    DW_OP_HP_mod_range   = 0xe4,
+    DW_OP_HP_unmod_range = 0xe5,
+    DW_OP_HP_tls         = 0xe6,
+    /* PGI (STMicroelectronics) extensions.  */
+    DW_OP_PGI_omp_thread_num = 0xf8
+  };
+
+/* Type encodings.  */
+enum dwarf_type
+  {
+    DW_ATE_void = 0x0,
+    DW_ATE_address = 0x1,
+    DW_ATE_boolean = 0x2,
+    DW_ATE_complex_float = 0x3,
+    DW_ATE_float = 0x4,
+    DW_ATE_signed = 0x5,
+    DW_ATE_signed_char = 0x6,
+    DW_ATE_unsigned = 0x7,
+    DW_ATE_unsigned_char = 0x8,
+    /* DWARF 3.  */
+    DW_ATE_imaginary_float = 0x9,
+    DW_ATE_packed_decimal = 0xa,
+    DW_ATE_numeric_string = 0xb,
+    DW_ATE_edited = 0xc,
+    DW_ATE_signed_fixed = 0xd,
+    DW_ATE_unsigned_fixed = 0xe,
+    DW_ATE_decimal_float = 0xf,
+    /* DWARF 4.  */
+    DW_ATE_UTF = 0x10,
+
+    DW_ATE_lo_user = 0x80,
+    DW_ATE_hi_user = 0xff,
+
+    /* HP extensions.  */
+    DW_ATE_HP_float80             = 0x80, /* Floating-point (80 bit).  */
+    DW_ATE_HP_complex_float80     = 0x81, /* Complex floating-point (80 bit).  */
+    DW_ATE_HP_float128            = 0x82, /* Floating-point (128 bit).  */
+    DW_ATE_HP_complex_float128    = 0x83, /* Complex fp (128 bit).  */
+    DW_ATE_HP_floathpintel        = 0x84, /* Floating-point (82 bit IA64).  */
+    DW_ATE_HP_imaginary_float80   = 0x85,
+    DW_ATE_HP_imaginary_float128  = 0x86,
+    DW_ATE_HP_VAX_float           = 0x88, /* F or G floating.  */
+    DW_ATE_HP_VAX_float_d         = 0x89, /* D floating.  */
+    DW_ATE_HP_packed_decimal      = 0x8a, /* Cobol.  */
+    DW_ATE_HP_zoned_decimal       = 0x8b, /* Cobol.  */
+    DW_ATE_HP_edited              = 0x8c, /* Cobol.  */
+    DW_ATE_HP_signed_fixed        = 0x8d, /* Cobol.  */
+    DW_ATE_HP_unsigned_fixed      = 0x8e, /* Cobol.  */
+    DW_ATE_HP_VAX_complex_float   = 0x8f, /* F or G floating complex.  */
+    DW_ATE_HP_VAX_complex_float_d = 0x90  /* D floating complex.  */
+  };
+
+/* Decimal sign encodings.  */
+enum dwarf_decimal_sign_encoding
+  {
+    /* DWARF 3.  */
+    DW_DS_unsigned = 0x01,
+    DW_DS_leading_overpunch = 0x02,
+    DW_DS_trailing_overpunch = 0x03,
+    DW_DS_leading_separate = 0x04,
+    DW_DS_trailing_separate = 0x05
+  };
+
+/* Endianity encodings.  */
+enum dwarf_endianity_encoding
+  {
+    /* DWARF 3.  */
+    DW_END_default = 0x00,
+    DW_END_big = 0x01,
+    DW_END_little = 0x02,
+
+    DW_END_lo_user = 0x40,
+    DW_END_hi_user = 0xff
+  };
+
+/* Array ordering names and codes.  */
+enum dwarf_array_dim_ordering
+  {
+    DW_ORD_row_major = 0,
+    DW_ORD_col_major = 1
+  };
+
+/* Access attribute.  */
+enum dwarf_access_attribute
+  {
+    DW_ACCESS_public = 1,
+    DW_ACCESS_protected = 2,
+    DW_ACCESS_private = 3
+  };
+
+/* Visibility.  */
+enum dwarf_visibility_attribute
+  {
+    DW_VIS_local = 1,
+    DW_VIS_exported = 2,
+    DW_VIS_qualified = 3
+  };
+
+/* Virtuality.  */
+enum dwarf_virtuality_attribute
+  {
+    DW_VIRTUALITY_none = 0,
+    DW_VIRTUALITY_virtual = 1,
+    DW_VIRTUALITY_pure_virtual = 2
+  };
+
+/* Case sensitivity.  */
+enum dwarf_id_case
+  {
+    DW_ID_case_sensitive = 0,
+    DW_ID_up_case = 1,
+    DW_ID_down_case = 2,
+    DW_ID_case_insensitive = 3
+  };
+
+/* Calling convention.  */
+enum dwarf_calling_convention
+  {
+    DW_CC_normal = 0x1,
+    DW_CC_program = 0x2,
+    DW_CC_nocall = 0x3,
+
+    DW_CC_lo_user = 0x40,
+    DW_CC_hi_user = 0xff,
+
+    DW_CC_GNU_renesas_sh = 0x40,
+    DW_CC_GNU_borland_fastcall_i386 = 0x41,
+
+    /* This DW_CC_ value is not currently generated by any toolchain.  It is
+       used internally to GDB to indicate OpenCL C functions that have been
+       compiled with the IBM XL C for OpenCL compiler and use a non-platform
+       calling convention for passing OpenCL C vector types.  This value may
+       be changed freely as long as it does not conflict with any other DW_CC_
+       value defined here.  */
+    DW_CC_GDB_IBM_OpenCL = 0xff
+  };
+
+/* Inline attribute.  */
+enum dwarf_inline_attribute
+  {
+    DW_INL_not_inlined = 0,
+    DW_INL_inlined = 1,
+    DW_INL_declared_not_inlined = 2,
+    DW_INL_declared_inlined = 3
+  };
+
+/* Discriminant lists.  */
+enum dwarf_discrim_list
+  {
+    DW_DSC_label = 0,
+    DW_DSC_range = 1
+  };
+
+/* Line number opcodes.  */
+enum dwarf_line_number_ops
+  {
+    DW_LNS_extended_op = 0,
+    DW_LNS_copy = 1,
+    DW_LNS_advance_pc = 2,
+    DW_LNS_advance_line = 3,
+    DW_LNS_set_file = 4,
+    DW_LNS_set_column = 5,
+    DW_LNS_negate_stmt = 6,
+    DW_LNS_set_basic_block = 7,
+    DW_LNS_const_add_pc = 8,
+    DW_LNS_fixed_advance_pc = 9,
+    /* DWARF 3.  */
+    DW_LNS_set_prologue_end = 10,
+    DW_LNS_set_epilogue_begin = 11,
+    DW_LNS_set_isa = 12
+  };
+
+/* Line number extended opcodes.  */
+enum dwarf_line_number_x_ops
+  {
+    DW_LNE_end_sequence = 1,
+    DW_LNE_set_address = 2,
+    DW_LNE_define_file = 3,
+    DW_LNE_set_discriminator = 4,
+    /* HP extensions.  */
+    DW_LNE_HP_negate_is_UV_update      = 0x11,
+    DW_LNE_HP_push_context             = 0x12,
+    DW_LNE_HP_pop_context              = 0x13,
+    DW_LNE_HP_set_file_line_column     = 0x14,
+    DW_LNE_HP_set_routine_name         = 0x15,
+    DW_LNE_HP_set_sequence             = 0x16,
+    DW_LNE_HP_negate_post_semantics    = 0x17,
+    DW_LNE_HP_negate_function_exit     = 0x18,
+    DW_LNE_HP_negate_front_end_logical = 0x19,
+    DW_LNE_HP_define_proc              = 0x20,
+    DW_LNE_HP_source_file_correlation  = 0x80,
+
+    DW_LNE_lo_user = 0x80,
+    DW_LNE_hi_user = 0xff
+  };
+
+/* Sub-opcodes for DW_LNE_HP_source_file_correlation.  */
+enum dwarf_line_number_hp_sfc_ops
+  {
+    DW_LNE_HP_SFC_formfeed = 1,
+    DW_LNE_HP_SFC_set_listing_line = 2,
+    DW_LNE_HP_SFC_associate = 3
+  };
+
+/* Call frame information.  */
+enum dwarf_call_frame_info
+  {
+    DW_CFA_advance_loc = 0x40,
+    DW_CFA_offset = 0x80,
+    DW_CFA_restore = 0xc0,
+    DW_CFA_nop = 0x00,
+    DW_CFA_set_loc = 0x01,
+    DW_CFA_advance_loc1 = 0x02,
+    DW_CFA_advance_loc2 = 0x03,
+    DW_CFA_advance_loc4 = 0x04,
+    DW_CFA_offset_extended = 0x05,
+    DW_CFA_restore_extended = 0x06,
+    DW_CFA_undefined = 0x07,
+    DW_CFA_same_value = 0x08,
+    DW_CFA_register = 0x09,
+    DW_CFA_remember_state = 0x0a,
+    DW_CFA_restore_state = 0x0b,
+    DW_CFA_def_cfa = 0x0c,
+    DW_CFA_def_cfa_register = 0x0d,
+    DW_CFA_def_cfa_offset = 0x0e,
+    /* DWARF 3.  */
+    DW_CFA_def_cfa_expression = 0x0f,
+    DW_CFA_expression = 0x10,
+    DW_CFA_offset_extended_sf = 0x11,
+    DW_CFA_def_cfa_sf = 0x12,
+    DW_CFA_def_cfa_offset_sf = 0x13,
+    DW_CFA_val_offset = 0x14,
+    DW_CFA_val_offset_sf = 0x15,
+    DW_CFA_val_expression = 0x16,
+
+    DW_CFA_lo_user = 0x1c,
+    DW_CFA_hi_user = 0x3f,
+
+    /* SGI/MIPS specific.  */
+    DW_CFA_MIPS_advance_loc8 = 0x1d,
+    /* GNU extensions.  */
+    DW_CFA_GNU_window_save = 0x2d,
+    DW_CFA_GNU_args_size = 0x2e,
+    DW_CFA_GNU_negative_offset_extended = 0x2f
+  };
+
+#define DW_CIE_ID        0xffffffff
+#define DW64_CIE_ID      0xffffffffffffffffULL
+#define DW_CIE_VERSION   1
+
+#define DW_CFA_extended   0
+
+#define DW_CHILDREN_no              0x00
+#define DW_CHILDREN_yes                     0x01
+
+#define DW_ADDR_none           0
+
+/* Source language names and codes.  */
+enum dwarf_source_language
+  {
+    DW_LANG_C89 = 0x0001,
+    DW_LANG_C = 0x0002,
+    DW_LANG_Ada83 = 0x0003,
+    DW_LANG_C_plus_plus = 0x0004,
+    DW_LANG_Cobol74 = 0x0005,
+    DW_LANG_Cobol85 = 0x0006,
+    DW_LANG_Fortran77 = 0x0007,
+    DW_LANG_Fortran90 = 0x0008,
+    DW_LANG_Pascal83 = 0x0009,
+    DW_LANG_Modula2 = 0x000a,
+    /* DWARF 3.  */
+    DW_LANG_Java = 0x000b,
+    DW_LANG_C99 = 0x000c,
+    DW_LANG_Ada95 = 0x000d,
+    DW_LANG_Fortran95 = 0x000e,
+    DW_LANG_PLI = 0x000f,
+    DW_LANG_ObjC = 0x0010,
+    DW_LANG_ObjC_plus_plus = 0x0011,
+    DW_LANG_UPC = 0x0012,
+    DW_LANG_D = 0x0013,
+    /* DWARF 4.  */
+    DW_LANG_Python = 0x0014,
+    /* DWARF 5.  */
+    DW_LANG_Go = 0x0016,
+
+    DW_LANG_lo_user = 0x8000,  /* Implementation-defined range start.  */
+    DW_LANG_hi_user = 0xffff,  /* Implementation-defined range start.  */
+
+    /* MIPS.  */
+    DW_LANG_Mips_Assembler = 0x8001,
+    /* UPC.  */
+    DW_LANG_Upc = 0x8765,
+    /* HP extensions.  */
+    DW_LANG_HP_Bliss     = 0x8003,
+    DW_LANG_HP_Basic91   = 0x8004,
+    DW_LANG_HP_Pascal91  = 0x8005,
+    DW_LANG_HP_IMacro    = 0x8006,
+    DW_LANG_HP_Assembler = 0x8007
+  };
+
+/* Names and codes for macro information.  */
+enum dwarf_macinfo_record_type
+  {
+    DW_MACINFO_define = 1,
+    DW_MACINFO_undef = 2,
+    DW_MACINFO_start_file = 3,
+    DW_MACINFO_end_file = 4,
+    DW_MACINFO_vendor_ext = 255
+  };
+
+/* Names and codes for new style macro information.  */
+enum dwarf_macro_record_type
+  {
+    DW_MACRO_GNU_define = 1,
+    DW_MACRO_GNU_undef = 2,
+    DW_MACRO_GNU_start_file = 3,
+    DW_MACRO_GNU_end_file = 4,
+    DW_MACRO_GNU_define_indirect = 5,
+    DW_MACRO_GNU_undef_indirect = 6,
+    DW_MACRO_GNU_transparent_include = 7,
+    DW_MACRO_GNU_lo_user = 0xe0,
+    DW_MACRO_GNU_hi_user = 0xff
+  };
+\f
+/* @@@ For use with GNU frame unwind information.  */
+
+#define DW_EH_PE_absptr                0x00
+#define DW_EH_PE_omit          0xff
+
+#define DW_EH_PE_uleb128       0x01
+#define DW_EH_PE_udata2                0x02
+#define DW_EH_PE_udata4                0x03
+#define DW_EH_PE_udata8                0x04
+#define DW_EH_PE_sleb128       0x09
+#define DW_EH_PE_sdata2                0x0A
+#define DW_EH_PE_sdata4                0x0B
+#define DW_EH_PE_sdata8                0x0C
+#define DW_EH_PE_signed                0x08
+
+#define DW_EH_PE_pcrel         0x10
+#define DW_EH_PE_textrel       0x20
+#define DW_EH_PE_datarel       0x30
+#define DW_EH_PE_funcrel       0x40
+#define DW_EH_PE_aligned       0x50
+
+#define DW_EH_PE_indirect      0x80
+
+#endif /* _DWARF2_H */
diff --git a/dwz.c b/dwz.c
new file mode 100644 (file)
index 0000000..b7a35e8
--- /dev/null
+++ b/dwz.c
@@ -0,0 +1,6329 @@
+/* Copyright (C) 2001-2012 Red Hat, Inc.
+   Written by Jakub Jelinek <jakub@redhat.com>, 2012.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <errno.h>
+#include <error.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <obstack.h>
+#include <stdbool.h>
+
+#include <gelf.h>
+#include "dwarf2.h"
+#include "hashtab.h"
+
+#ifndef IGNORE_LOCUS
+#define IGNORE_LOCUS 0
+#endif
+
+#define obstack_chunk_alloc     xmalloc
+#define obstack_chunk_free      free
+
+static void *
+xmalloc (size_t size)
+{
+  void *newmem = malloc (size);
+  if (!newmem)
+    error (1, ENOMEM, "Could not allocate memory");
+  return newmem;
+}
+
+/* General obstack for struct dw_cu, dw_die, dw_toplevel_die,
+   also used for temporary vectors.  */
+static struct obstack ob;
+
+/* String obstack for .debug_line filenames.  */
+static struct obstack strob;
+
+typedef struct
+{
+  Elf *elf;
+  GElf_Ehdr ehdr;
+  Elf_Scn **scn;
+  const char *filename;
+  int lastscn;
+  GElf_Shdr shdr[0];
+} DSO;
+
+/* Macro to parse an uleb128 number, return it and
+   update ptr to the end of the uleb128 at the same time.  */
+#define read_uleb128(ptr) ({           \
+  uint64_t ret = 0;                    \
+  uint64_t c;                          \
+  int shift = 0;                       \
+  do                                   \
+    {                                  \
+      c = *ptr++;                      \
+      ret |= (c & 0x7f) << shift;      \
+      shift += 7;                      \
+    } while (c & 0x80);                        \
+                                       \
+  if (shift >= 70)                     \
+    ret = ~(uint64_t) 0;               \
+  ret;                                 \
+})
+
+/* Macro to parse a sleb128 number, return it and
+   update ptr to the end of the sleb128 at the same time.  */
+#define read_sleb128(ptr) ({           \
+  uint64_t ret = 0;                    \
+  uint64_t c;                          \
+  int shift = 0;                       \
+  do                                   \
+    {                                  \
+      c = *ptr++;                      \
+      ret |= (c & 0x7f) << shift;      \
+      shift += 7;                      \
+    } while (c & 0x80);                        \
+                                       \
+  if (shift >= 70)                     \
+    ret = ~(uint64_t) 0;               \
+  else if (c & 0x40)                   \
+    ret |= (-(uint64_t) 1) << shift;   \
+  ret;                                 \
+})
+
+/* Macro to store an uleb128 number to ptr and update
+   ptr to point after it.  */
+#define write_uleb128(ptr, val)                \
+  do                                   \
+    {                                  \
+      uint64_t valv = (val);           \
+      do                               \
+       {                               \
+         unsigned char c = valv & 0x7f;\
+         valv >>= 7;                   \
+         if (valv)                     \
+           c |= 0x80;                  \
+         *ptr++ = c;                   \
+       }                               \
+      while (valv);                    \
+    }                                  \
+  while (0)
+
+/* Pointer size in the debug info, in bytes.  Only debug info
+   with a single pointer size are handled.  */
+static int ptr_size;
+
+/* Utility functions and macros for reading/writing values in
+   given ELF endianity, which might be different from host endianity.
+   No specific alignment is expected.  */
+static uint16_t (*do_read_16) (unsigned char *ptr);
+static uint32_t (*do_read_32) (unsigned char *ptr);
+static uint64_t (*do_read_64) (unsigned char *ptr);
+static void (*do_write_16) (unsigned char *ptr, unsigned short val);
+static void (*do_write_32) (unsigned char *ptr, unsigned int val);
+static void (*do_write_64) (unsigned char *ptr, uint64_t val);
+
+static inline uint16_t
+buf_read_ule16 (unsigned char *data)
+{
+  return data[0] | (data[1] << 8);
+}
+
+static inline uint16_t
+buf_read_ube16 (unsigned char *data)
+{
+  return data[1] | (data[0] << 8);
+}
+
+static inline uint32_t
+buf_read_ule32 (unsigned char *data)
+{
+  return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+static inline uint32_t
+buf_read_ube32 (unsigned char *data)
+{
+  return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
+}
+
+static inline uint64_t
+buf_read_ule64 (unsigned char *data)
+{
+  return buf_read_ule32 (data)
+        | (((uint64_t) buf_read_ule32 (data + 4)) << 32);
+}
+
+static inline uint64_t
+buf_read_ube64 (unsigned char *data)
+{
+  return (((uint64_t) buf_read_ube32 (data)) << 32)
+        | buf_read_ube32 (data + 4);
+}
+
+#define read_8(ptr) *ptr++
+
+#define read_16(ptr) ({                                        \
+  uint16_t ret = do_read_16 (ptr);                     \
+  ptr += 2;                                            \
+  ret;                                                 \
+})
+
+#define read_32(ptr) ({                                        \
+  uint32_t ret = do_read_32 (ptr);                     \
+  ptr += 4;                                            \
+  ret;                                                 \
+})
+
+#define read_64(ptr) ({                                        \
+  uint64_t ret = do_read_64 (ptr);                     \
+  ptr += 8;                                            \
+  ret;                                                 \
+})
+
+#define write_8(ptr, val)                              \
+  do                                                   \
+    *ptr++ = (val);                                    \
+  while (0)
+
+#define write_16(ptr, val)                             \
+  do                                                   \
+    {                                                  \
+      do_write_16 (ptr, val);                          \
+      ptr += 2;                                                \
+    }                                                  \
+  while (0)
+
+#define write_32(ptr, val)                             \
+  do                                                   \
+    {                                                  \
+      do_write_32 (ptr, val);                          \
+      ptr += 4;                                                \
+    }                                                  \
+  while (0)
+
+#define write_64(ptr, val)                             \
+  do                                                   \
+    {                                                  \
+      do_write_64 (ptr, val);                          \
+      ptr += 8;                                                \
+    }                                                  \
+  while (0)
+
+static uint64_t
+read_size (unsigned char *p, int size)
+{
+  switch (size)
+    {
+    case 1: return read_8 (p);
+    case 2: return read_16 (p);
+    case 4: return read_32 (p);
+    case 8: return read_64 (p);
+    default: abort ();
+    }
+}
+
+static void
+write_size (unsigned char *p, int size, uint64_t val)
+{
+  switch (size)
+    {
+    case 1: write_8 (p, val); break;
+    case 2: write_16 (p, val); break;
+    case 4: write_32 (p, val); break;
+    case 8: write_64 (p, val); break;
+    default: abort ();
+    }
+}
+
+static void
+buf_write_le16 (unsigned char *p, unsigned short v)
+{
+  p[0] = v;
+  p[1] = v >> 8;
+}
+
+static void
+buf_write_be16 (unsigned char *p, unsigned short v)
+{
+  p[1] = v;
+  p[0] = v >> 8;
+}
+
+static void
+buf_write_le32 (unsigned char *p, unsigned int v)
+{
+  p[0] = v;
+  p[1] = v >> 8;
+  p[2] = v >> 16;
+  p[3] = v >> 24;
+}
+
+static void
+buf_write_be32 (unsigned char *p, unsigned int v)
+{
+  p[3] = v;
+  p[2] = v >> 8;
+  p[1] = v >> 16;
+  p[0] = v >> 24;
+}
+
+static void
+buf_write_le64 (unsigned char *data, uint64_t v)
+{
+  buf_write_le32 (data, v);
+  buf_write_le32 (data + 4, v >> 32);
+}
+
+static void
+buf_write_be64 (unsigned char *data, uint64_t v)
+{
+  buf_write_be32 (data, v >> 32);
+  buf_write_be32 (data + 4, v);
+}
+
+/* Return a string from section SEC at offset OFFSET.  */
+static const char *
+strptr (DSO *dso, int sec, off_t offset)
+{
+  Elf_Scn *scn;
+  Elf_Data *data;
+
+  scn = dso->scn[sec];
+  if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size)
+    {
+      data = NULL;
+      while ((data = elf_rawdata (scn, data)) != NULL)
+       {
+         if (data->d_buf
+             && offset >= data->d_off
+             && offset < (off_t) (data->d_off + data->d_size))
+           return (const char *) data->d_buf + (offset - data->d_off);
+       }
+    }
+
+  return NULL;
+}
+
+/* Details about standard DWARF sections.  */
+static struct
+  {
+    const char *name;
+    unsigned char *data;
+    size_t size;
+    int sec;
+  } debug_sections[] =
+  {
+#define DEBUG_INFO     0
+#define DEBUG_ABBREV   1
+#define DEBUG_LINE     2
+#define DEBUG_ARANGES  3
+#define DEBUG_PUBNAMES 4
+#define DEBUG_PUBTYPES 5
+#define DEBUG_MACINFO  6
+#define DEBUG_LOC      7
+#define DEBUG_STR      8
+#define DEBUG_FRAME    9
+#define DEBUG_RANGES   10
+#define DEBUG_TYPES    11
+#define DEBUG_MACRO    12
+    { ".debug_info", NULL, 0, 0 },
+    { ".debug_abbrev", NULL, 0, 0 },
+    { ".debug_line", NULL, 0, 0 },
+    { ".debug_aranges", NULL, 0, 0 },
+    { ".debug_pubnames", NULL, 0, 0 },
+    { ".debug_pubtypes", NULL, 0, 0 },
+    { ".debug_macinfo", NULL, 0, 0 },
+    { ".debug_loc", NULL, 0, 0 },
+    { ".debug_str", NULL, 0, 0 },
+    { ".debug_frame", NULL, 0, 0 },
+    { ".debug_ranges", NULL, 0, 0 },
+    { ".debug_types", NULL, 0, 0 },
+    { ".debug_macro", NULL, 0, 0 },
+    { NULL, NULL, 0, 0 }
+  };
+
+/* A single attribute in abbreviations.  */
+struct abbrev_attr
+{
+  /* DW_AT_* attribute code.  */
+  unsigned int attr;
+  /* DW_FORM_* form code.  */
+  unsigned int form;
+};
+
+/* Internal structure for .debug_abbrev entries.  */
+struct abbrev_tag
+{
+  /* Abbreviation number.  */
+  unsigned int entry;
+  /* Hash value, in cu->cu_abbrev hash tables it is
+     the same as entry, in cu->cu_new_abbrev hash tables
+     iterative hash of all the relevant values.  */
+  hashval_t hash;
+  /* DW_TAG_* code.  */
+  unsigned int tag;
+  /* Number of attributes.  */
+  unsigned int nattr;
+  /* How many DIEs refer to this abbreviation (unused
+     in cu->cu_abbrev hash tables).  */
+  unsigned int nusers;
+  /* True if DIEs with this abbreviation have children.  */
+  bool children;
+  /* True if any typed DWARF opcodes refer to this.  */
+  bool op_type_referenced;
+  /* Attribute/form pairs.  */
+  struct abbrev_attr attr[0];
+};
+
+typedef struct dw_die *dw_die_ref;
+struct import_cu;
+
+/* An entry from .debug_line file table.  */
+struct dw_file
+{
+  char *dir;
+  char *file;
+  uint64_t time, size;
+};
+
+/* Internal representation of a compilation (or partial)
+   unit.  */
+struct dw_cu
+{
+  /* Abbreviation hash table, or NULL for newly created
+     partial CUs.  */
+  htab_t cu_abbrev;
+  /* Cached entries from .debug_line file table.  */
+  struct dw_file *cu_files;
+  unsigned int cu_nfiles;
+  /* CUs linked from first_cu through this chain.  */
+  struct dw_cu *cu_next;
+  /* Offset in original .debug_info if cu_abbrev is non-NULL,
+     otherwise a unique index of the newly created partial CU.  */
+  unsigned int cu_offset;
+  /* DWARF version of the CU.  */
+  unsigned int cu_version;
+  /* Cached DW_AT_comp_dir value from DW_TAG_*_unit cu_die,
+     or NULL if that attribute is not present.  */
+  char *cu_comp_dir;
+  /* Pointer to the DW_TAG_*_unit inside of the .debug_info
+     chunk.  */
+  dw_die_ref cu_die;
+  /* New abbreviation hash table.  */
+  htab_t cu_new_abbrev;
+  union dw_cu_u1
+    {
+      /* Pointer to another struct dw_cu that owns
+        cu_new_abbrev for this CU.  */
+      struct dw_cu *cu_new_abbrev_owner;
+      /* Pointer used during create_import_tree.  */
+      struct import_cu *cu_icu;
+    } u1;
+  union dw_cu_u2
+    {
+      /* Offset into the new .debug_abbrev section.  */
+      unsigned int cu_new_abbrev_offset;
+      /* Highest ->entry value in the new abbrev table
+        For abbrevs computed by this tool it is always
+        equal to the number of abbreviations, but if
+        abbrevs are read for .debug_types section which
+        is not rewritten, there might be holes.  */
+      unsigned int cu_largest_entry;
+    } u2;
+  /* Offset into the new .debug_info section.  */
+  unsigned int cu_new_offset;
+};
+
+/* Internal representation of a debugging information entry (DIE).
+   This structure should be kept as small as possible,
+   there are .debug_info sections with tens of millions of DIEs
+   in them and this structure is allocated for each of them.  */
+struct dw_die
+{
+  /* This field should be the first, otherwise die_dup and die_nextdup
+     macros need adjustment.  Pointer to the CU this DIE is in or
+     will be in.  */
+  struct dw_cu *die_cu;
+  /* Tree pointers, to parent, first child and pointer to next sibling.  */
+  dw_die_ref die_parent, die_child, die_sib;
+  /* Pointer to the old .debug_abbrev entry's internal representation.  */
+  struct abbrev_tag *die_abbrev;
+  /* Offset in old .debug_info from the start of the .debug_info section,
+     -1U for newly created DIEs.  */
+  unsigned int die_offset;
+  /* Size of the DIE (abbrev number + attributes), not including children.
+     In later phases this holds the new size as opposed to the old one.  */
+  unsigned int die_size;
+  /* Cached copy of die_abbrev->tag.  */
+  enum dwarf_tag die_tag : 16;
+  /* State of checksum computation.  Not computed yet, computed and
+     suitable for moving into partial units, currently being computed
+     and finally determined unsuitable for moving into partial units.  */
+  enum { CK_UNKNOWN, CK_KNOWN, CK_BEING_COMPUTED, CK_BAD } die_ck_state : 2;
+  /* Set if any DW_OP_call2 opcode refers to this DIE.  */
+  unsigned int die_op_call2_referenced : 1;
+  /* Set if any DW_OP_GNU_{{regval,deref,const}_type,convert,reinterpret}
+     opcode refers to this DIE.  Only DW_TAG_base_type DIEs should be
+     referenced.  As those opcodes refer to them using uleb128, we need to try
+     hard to have those DIEs with low enough offsets that the uleb128 will
+     fit.  */
+  unsigned int die_op_type_referenced : 1;
+  /* Set in DW_TAG_namespace or DW_TAG_module with DW_AT_name that is
+     either a child of DW_TAG_*_unit, or a child of another
+     die_named_namespace DIE.  */
+  unsigned int die_named_namespace : 1;
+  /* Set if u.p1.die_ref_hash is valid.  */
+  unsigned int die_ref_hash_computed : 1;
+  /* Set if die_dup and die_nextdup fields are after this structure.
+     True for DW_TAG_*_unit DIEs, die_named_namespace DIEs and their
+     immediate children.  */
+  unsigned int die_toplevel : 1;
+  /* Set if we want to remove this DIE from its containing CU.  */
+  unsigned int die_remove : 1;
+  /* Index into the dw_die_ref vector used in checksum_ref_die function.
+     While this is only phase 1 field, we get better packing by having it
+     here instead of u.p1.  */
+  unsigned int die_ref_seen;
+  union dw_die_phase
+    {
+      /* Fields used in the first phase (read_debug_info and functions
+        it calls).  */
+      struct dw_die_p1
+       {
+         /* Iterative hash value of the tag, attributes other than
+            references or DW_FORM_ref_addr references or references
+            within the subtree of ultimate parent's die_toplevel DIE's
+            children.  Computed by checksum_die function.  */
+         hashval_t die_hash;
+         /* Iterative hash of other references.  Computed by
+            checksum_ref_die function.  */
+         hashval_t die_ref_hash;
+         /* Tick count of entering and leaving a DIE during depth first
+            traversal of the CU, used to quickly find if a subtree is
+            referenced.  */
+         unsigned int die_enter, die_exit;
+       } p1;
+      /* Fields used only after the first phase (from compute_abbrevs
+        till the end).  */
+      struct dw_die_p2
+       {
+         /* Pointer to internal representation of new .debug_abbrev
+            entry for this DIE.  */
+         struct abbrev_tag *die_new_abbrev;
+         /* Offset within the new .debug_info CU.  Unlike die_offset
+            this one is CU relative, so die_cu->cu_new_offset needs
+            to be added to it to get .debug_info offset.  */
+         unsigned int die_new_offset;
+         /* Used during compute_abbrevs DW_FORM_ref_udata optimization.  */
+         unsigned int die_intracu_udata_size;
+       } p2;
+     } u;
+};
+
+/* If die_toplevel is set, we allocate this structure instead.  */
+struct dw_toplevel_die
+{
+  /* Must be first.  */
+  struct dw_die die;
+  /* Pointer to a duplicate DIE.  */
+  dw_die_ref die_dup;
+  /* Chain of duplicate DIEs.  If die_dup is NULL, but die_nextdup
+     is non-NULL, this is the reference DIE of the duplicates.
+     All DIEs in the die->nextdup linked list have die_dup pointing
+     to this node.  The reference DIE is initially just a DIE in the
+     lowest CU that has the matching DIE, later on it is a DIE in
+     the newly created partial unit CU.  */
+  dw_die_ref die_nextdup;
+};
+
+/* Accessors of die_dup and die_nextdup field.  die->die_toplevel
+   must be set to use these two.  */
+#define die_dup(die) \
+  (((struct dw_toplevel_die *) (&(die)->die_cu))->die_dup)
+#define die_nextdup(die) \
+  (((struct dw_toplevel_die *) (&(die)->die_cu))->die_nextdup)
+/* Safe variant that check die_toplevel.  Can't be used on LHS.  */
+#define die_safe_dup(die) \
+  ((die)->die_toplevel ? die_dup (die) : (dw_die_ref) NULL)
+#define die_safe_nextdup(die) \
+  ((die)->die_toplevel ? die_nextdup (die) : (dw_die_ref) NULL)
+
+/* Hash function in cu->cu_abbrev as well as cu->cu_new_abbrev htab.  */
+static hashval_t
+abbrev_hash (const void *p)
+{
+  struct abbrev_tag *t = (struct abbrev_tag *)p;
+
+  return t->hash;
+}
+
+/* Equality function in cu->cu_abbrev htab.  */
+static int
+abbrev_eq (const void *p, const void *q)
+{
+  struct abbrev_tag *t1 = (struct abbrev_tag *)p;
+  struct abbrev_tag *t2 = (struct abbrev_tag *)q;
+
+  return t1->entry == t2->entry;
+}
+
+/* Delete function in cu->cu_abbrev htab.  */
+static void
+abbrev_del (void *p)
+{
+  free (p);
+}
+
+/* Equality function in cu->cu_new_abbrev htab.  */
+static int
+abbrev_eq2 (const void *p, const void *q)
+{
+  struct abbrev_tag *t1 = (struct abbrev_tag *)p;
+  struct abbrev_tag *t2 = (struct abbrev_tag *)q;
+  unsigned int i;
+
+  if (t1->hash != t2->hash
+      || t1->tag != t2->tag
+      || t1->nattr != t2->nattr
+      || t1->children != t2->children)
+    return 0;
+  for (i = 0; i < t1->nattr; i++)
+    if (t1->attr[i].attr != t2->attr[i].attr
+       || t1->attr[i].form != t2->attr[i].form)
+      return 0;
+  return 1;
+}
+
+/* Helper function to compute abbrev entry iterative hash value.  */
+static void
+compute_abbrev_hash (struct abbrev_tag *t)
+{
+  unsigned int i;
+
+  t->hash = iterative_hash_object (t->tag, 0);
+  t->hash = iterative_hash_object (t->nattr, t->hash);
+  t->hash = iterative_hash_object (t->children, t->hash);
+  for (i = 0; i < t->nattr; i++)
+    {
+      t->hash = iterative_hash_object (t->attr[i].attr, t->hash);
+      t->hash = iterative_hash_object (t->attr[i].form, t->hash);
+    }
+}
+
+/* Maximum number of attributes in a DIE.  */
+static unsigned int max_nattr;
+
+/* Parse a .debug_abbrev entry at PTR.  If !DEBUG_TYPES, return
+   cu->cu_abbrev style hash table, otherwise return cu->cu_new_abbrev
+   style hash table (this is used for reading .debug_types abbreviations
+   which we are not rewriting).  */
+static htab_t
+read_abbrev (DSO *dso, unsigned char *ptr, bool debug_types)
+{
+  htab_t h;
+  unsigned int attr, form;
+  struct abbrev_tag *t;
+  unsigned int size;
+  void **slot;
+
+  if (debug_types)
+    h = htab_try_create (20, abbrev_hash, abbrev_eq2, NULL);
+  else
+    h = htab_try_create (50, abbrev_hash, abbrev_eq, abbrev_del);
+  if (h == NULL)
+    {
+no_memory:
+      error (0, ENOMEM, "%s: Could not read .debug_abbrev", dso->filename);
+      if (h)
+       {
+         h->del_f = abbrev_del;
+         htab_delete (h);
+       }
+      return NULL;
+    }
+
+  while ((attr = read_uleb128 (ptr)) != 0)
+    {
+      size = 10;
+      t = xmalloc (sizeof (*t) + size * sizeof (struct abbrev_attr));
+      t->entry = attr;
+      t->hash = attr;
+      t->nattr = 0;
+      t->nusers = 0;
+      t->tag = read_uleb128 (ptr);
+      t->children = *ptr++ == DW_CHILDREN_yes;
+      t->op_type_referenced = false;
+      while ((attr = read_uleb128 (ptr)) != 0)
+       {
+         if (t->nattr == size)
+           {
+             size += 10;
+             t = realloc (t, sizeof (*t) + size * sizeof (struct abbrev_attr));
+             if (t == NULL)
+               goto no_memory;
+           }
+         form = read_uleb128 (ptr);
+         if (form == 2
+             || (form > DW_FORM_flag_present && form != DW_FORM_ref_sig8))
+           {
+             error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form);
+             free (t);
+             h->del_f = abbrev_del;
+             htab_delete (h);
+             return NULL;
+           }
+
+         t->attr[t->nattr].attr = attr;
+         t->attr[t->nattr++].form = form;
+       }
+      if (read_uleb128 (ptr) != 0)
+       {
+         error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros",
+                dso->filename);
+         free (t);
+         h->del_f = abbrev_del;
+         htab_delete (h);
+         return NULL;
+       }
+      if (t->nattr > max_nattr)
+       max_nattr = t->nattr;
+      if (debug_types)
+       compute_abbrev_hash (t);
+      slot = htab_find_slot_with_hash (h, t, t->hash, INSERT);
+      if (slot == NULL)
+       {
+         free (t);
+         goto no_memory;
+       }
+      if (*slot != NULL)
+       {
+         error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename,
+                t->entry);
+         free (t);
+         h->del_f = abbrev_del;
+         htab_delete (h);
+         return NULL;
+       }
+      *slot = t;
+    }
+
+  return h;
+}
+
+/* Read the directory and file table from .debug_line offset OFF,
+   record it in CU.  */
+static int
+read_debug_line (DSO *dso, struct dw_cu *cu, uint32_t off)
+{
+  unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir, *file;
+  unsigned char **dirt;
+  unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size;
+  unsigned char *endcu, *endprol;
+  unsigned char opcode_base;
+  uint32_t value, dirt_cnt, file_cnt;
+
+  if (off >= debug_sections[DEBUG_LINE].size - 4)
+    {
+      error (0, 0, "%s: .debug_line reference above end of section",
+            dso->filename);
+      return 1;
+    }
+
+  ptr += off;
+
+  endcu = ptr + 4;
+  endcu += read_32 (ptr);
+  if (endcu == ptr + 0xffffffff)
+    {
+      error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+      return 1;
+    }
+
+  if (endcu > endsec)
+    {
+      error (0, 0, "%s: .debug_line CU does not fit into section",
+            dso->filename);
+      return 1;
+    }
+
+  value = read_16 (ptr);
+  if (value < 2 || value > 4)
+    {
+      error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
+            value);
+      return 1;
+    }
+
+  endprol = ptr + 4;
+  endprol += read_32 (ptr);
+  if (endprol > endcu)
+    {
+      error (0, 0, "%s: .debug_line CU prologue does not fit into CU",
+            dso->filename);
+      return 1;
+    }
+
+  opcode_base = ptr[4 + (value >= 4)];
+  ptr = dir = ptr + 4 + (value >= 4) + opcode_base;
+
+  /* dir table: */
+  value = 1;
+  while (*ptr != 0)
+    {
+      ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+      ++value;
+    }
+
+  dirt = (unsigned char **) alloca (value * sizeof (unsigned char *));
+  dirt[0] = NULL;
+  dirt_cnt = 1;
+  ptr = dir;
+  while (*ptr != 0)
+    {
+      dirt[dirt_cnt++] = ptr;
+      ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+    }
+  ptr++;
+
+  /* file table: */
+  file = ptr;
+  file_cnt = 0;
+  while (*ptr != 0)
+    {
+      ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+      value = read_uleb128 (ptr);
+
+      if (value >= dirt_cnt)
+       {
+         error (0, 0, "%s: Wrong directory table index %u",
+                dso->filename, value);
+         return 1;
+       }
+
+      read_uleb128 (ptr);
+      read_uleb128 (ptr);
+      file_cnt++;
+    }
+
+  cu->cu_nfiles = file_cnt;
+  cu->cu_files = calloc (file_cnt, sizeof (struct dw_file));
+  if (cu->cu_files == NULL)
+    {
+      error (0, ENOMEM, "Not enough memory");
+      return 1;
+    }
+
+  ptr = file;
+  file_cnt = 0;
+  while (*ptr != 0)
+    {
+      unsigned char *end;
+      cu->cu_files[file_cnt].file = (char *) ptr;
+      ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+      end = ptr;
+      value = read_uleb128 (ptr);
+
+      if (value >= dirt_cnt)
+       {
+         error (0, 0, "%s: Wrong directory table index %u",
+                dso->filename, value);
+         return 1;
+       }
+
+      cu->cu_files[file_cnt].dir = (char *) dirt[value];
+      cu->cu_files[file_cnt].time = read_uleb128 (ptr);
+      cu->cu_files[file_cnt].size = read_uleb128 (ptr);
+      if (cu->cu_files[file_cnt].file[0] != '/'
+         && cu->cu_files[file_cnt].dir != NULL)
+       {
+         size_t file_len = (char *) end - cu->cu_files[file_cnt].file;
+         size_t dir_len = strlen (cu->cu_files[file_cnt].dir);
+         if (dir_len)
+           {
+             obstack_grow (&strob, cu->cu_files[file_cnt].dir,
+                           dir_len);
+             if (cu->cu_files[file_cnt].dir[dir_len - 1] != '/')
+               obstack_1grow (&strob, '/');
+             obstack_grow (&strob, cu->cu_files[file_cnt].file,
+                           file_len);
+             cu->cu_files[file_cnt].file
+               = (char *) obstack_finish (&strob);
+             cu->cu_files[file_cnt].dir = NULL;
+           }
+       }
+      file_cnt++;
+    }
+
+  return 0;
+}
+
+/* Hash function for off_htab hash table.  */
+static hashval_t
+off_hash (const void *p)
+{
+  dw_die_ref die = (dw_die_ref) p;
+
+  return die->die_offset;
+}
+
+/* Equality function for off_htab hash table.  */
+static int
+off_eq (const void *p, const void *q)
+{
+  return ((dw_die_ref) p)->die_offset == ((dw_die_ref) q)->die_offset;
+}
+
+/* Hash table to map die_offset values to struct dw_die pointers.  */
+static htab_t off_htab;
+
+/* Function to add DIE into the hash table (and create the hash table
+   when not already created).  */
+static int
+off_htab_add_die (dw_die_ref die)
+{
+  void **slot;
+  if (off_htab == NULL)
+    {
+      off_htab = htab_try_create (100000, off_hash, off_eq, NULL);
+      if (off_htab == NULL)
+       {
+       no_memory:
+         error (0, ENOMEM, "Not enough memory");
+         return 1;
+       }
+    }
+
+  slot = htab_find_slot (off_htab, die, INSERT);
+  if (slot == NULL)
+    goto no_memory;
+  assert (*slot == NULL);
+  *slot = die;
+  return 0;
+}
+
+/* For DIE_OFFSET return dw_die_ref whose die_offset field is equal
+   to that value.  Return NULL if no DIE is at that position (buggy
+   DWARF input?).  */
+static dw_die_ref
+off_htab_lookup (unsigned int die_offset)
+{
+  struct dw_die die;
+  die.die_offset = die_offset;
+  return (dw_die_ref) htab_find_with_hash (off_htab, &die, die_offset);
+}
+
+/* Return a pointer at which DIE's attribute AT is encoded, and fill in
+   its form into *FORMP.  Return NULL if the attribute is not present.  */
+static unsigned char *
+get_AT (dw_die_ref die, enum dwarf_attribute at, enum dwarf_form *formp)
+{
+  struct abbrev_tag *t = die->die_abbrev;
+  unsigned int i;
+  unsigned char *ptr = debug_sections[DEBUG_INFO].data + die->die_offset;
+  read_uleb128 (ptr);
+  for (i = 0; i < t->nattr; ++i)
+    {
+      uint32_t form = t->attr[i].form;
+      size_t len = 0;
+
+      while (form == DW_FORM_indirect)
+       form = read_uleb128 (ptr);
+      if (t->attr[i].attr == at)
+       {
+         *formp = form;
+         return ptr;
+       }
+      switch (form)
+       {
+       case DW_FORM_ref_addr:
+         ptr += die->die_cu->cu_version == 2 ? ptr_size : 4;
+         break;
+       case DW_FORM_addr:
+         ptr += ptr_size;
+         break;
+       case DW_FORM_flag_present:
+         break;
+       case DW_FORM_ref1:
+       case DW_FORM_flag:
+       case DW_FORM_data1:
+         ++ptr;
+         break;
+       case DW_FORM_ref2:
+       case DW_FORM_data2:
+         ptr += 2;
+         break;
+       case DW_FORM_ref4:
+       case DW_FORM_data4:
+       case DW_FORM_sec_offset:
+       case DW_FORM_strp:
+         ptr += 4;
+         break;
+       case DW_FORM_ref8:
+       case DW_FORM_data8:
+       case DW_FORM_ref_sig8:
+         ptr += 8;
+         break;
+       case DW_FORM_sdata:
+       case DW_FORM_ref_udata:
+       case DW_FORM_udata:
+         read_uleb128 (ptr);
+         break;
+       case DW_FORM_string:
+         ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+         break;
+       case DW_FORM_indirect:
+         abort ();
+       case DW_FORM_block1:
+         len = *ptr++;
+         break;
+       case DW_FORM_block2:
+         len = read_16 (ptr);
+         form = DW_FORM_block1;
+         break;
+       case DW_FORM_block4:
+         len = read_32 (ptr);
+         form = DW_FORM_block1;
+         break;
+       case DW_FORM_block:
+       case DW_FORM_exprloc:
+         len = read_uleb128 (ptr);
+         form = DW_FORM_block1;
+         break;
+       default:
+         abort ();
+       }
+
+      if (form == DW_FORM_block1)
+       ptr += len;
+    }
+  return NULL;
+}
+
+/* Return an integer attribute AT of DIE.  Set *PRESENT to true
+   if found.  */
+static uint64_t
+get_AT_int (dw_die_ref die, enum dwarf_attribute at, bool *present)
+{
+  enum dwarf_form form;
+  unsigned char *ptr;
+  ptr = get_AT (die, at, &form);
+  *present = false;
+  if (ptr == NULL)
+    return 0;
+  *present = true;
+  switch (form)
+    {
+    case DW_FORM_ref_addr:
+      return read_size (ptr, die->die_cu->cu_version == 2 ? ptr_size : 4);
+    case DW_FORM_addr:
+      return read_size (ptr, ptr_size);
+    case DW_FORM_flag_present:
+      return 1;
+    case DW_FORM_ref1:
+    case DW_FORM_flag:
+    case DW_FORM_data1:
+      return read_8 (ptr);
+    case DW_FORM_ref2:
+    case DW_FORM_data2:
+      return read_16 (ptr);
+    case DW_FORM_ref4:
+    case DW_FORM_data4:
+    case DW_FORM_sec_offset:
+      return read_32 (ptr);
+    case DW_FORM_ref8:
+    case DW_FORM_data8:
+    case DW_FORM_ref_sig8:
+      return read_64 (ptr);
+    case DW_FORM_sdata:
+      return read_sleb128 (ptr);
+    case DW_FORM_ref_udata:
+    case DW_FORM_udata:
+      return read_uleb128 (ptr);
+    default:
+      *present = false;
+      return 0;
+    }
+}
+
+/* Return a pointer to string attribute AT in DIE, or NULL
+   if the attribute is not present.  */
+static char *
+get_AT_string (dw_die_ref die, enum dwarf_attribute at)
+{
+  enum dwarf_form form;
+  unsigned char *ptr;
+  ptr = get_AT (die, at, &form);
+  if (ptr == NULL)
+    return NULL;
+  switch (form)
+    {
+    case DW_FORM_string:
+      return (char *) ptr;
+    case DW_FORM_strp:
+      {
+       unsigned int strp = read_32 (ptr);
+       if (strp >= debug_sections[DEBUG_STR].size)
+         return NULL;
+       return (char *) debug_sections[DEBUG_STR].data + strp;
+      }
+    default:
+      return NULL;
+    }
+}
+
+/* Parse DWARF expression referenced or stored in DIE, starting at
+   PTR with LEN bytes.  Return non-zero on error.  If NEED_ADJUST
+   is non-NULL, set *NEED_ADJUST to true if it contains DIE references
+   that will need adjusting.  Some opcodes cause DIE or referenced
+   DIEs as unsuitable for moving into partial units, or limit their
+   location.  */
+static int
+read_exprloc (DSO *dso, dw_die_ref die, unsigned char *ptr, size_t len,
+             bool *need_adjust)
+{
+  unsigned char *end = ptr + len;
+  unsigned char op;
+  GElf_Addr addr;
+  dw_die_ref ref;
+
+  while (ptr < end)
+    {
+      op = *ptr++;
+      switch (op)
+       {
+       case DW_OP_addr:
+         ptr += ptr_size;
+         break;
+       case DW_OP_deref:
+       case DW_OP_dup:
+       case DW_OP_drop:
+       case DW_OP_over:
+       case DW_OP_swap:
+       case DW_OP_rot:
+       case DW_OP_xderef:
+       case DW_OP_abs:
+       case DW_OP_and:
+       case DW_OP_div:
+       case DW_OP_minus:
+       case DW_OP_mod:
+       case DW_OP_mul:
+       case DW_OP_neg:
+       case DW_OP_not:
+       case DW_OP_or:
+       case DW_OP_plus:
+       case DW_OP_shl:
+       case DW_OP_shr:
+       case DW_OP_shra:
+       case DW_OP_xor:
+       case DW_OP_eq:
+       case DW_OP_ge:
+       case DW_OP_gt:
+       case DW_OP_le:
+       case DW_OP_lt:
+       case DW_OP_ne:
+       case DW_OP_lit0 ... DW_OP_lit31:
+       case DW_OP_reg0 ... DW_OP_reg31:
+       case DW_OP_nop:
+       case DW_OP_push_object_address:
+       case DW_OP_form_tls_address:
+       case DW_OP_call_frame_cfa:
+       case DW_OP_stack_value:
+       case DW_OP_GNU_push_tls_address:
+       case DW_OP_GNU_uninit:
+         break;
+       case DW_OP_const1u:
+       case DW_OP_pick:
+       case DW_OP_deref_size:
+       case DW_OP_xderef_size:
+       case DW_OP_const1s:
+         ++ptr;
+         break;
+       case DW_OP_const2u:
+       case DW_OP_const2s:
+       case DW_OP_skip:
+       case DW_OP_bra:
+         ptr += 2;
+         break;
+       case DW_OP_call2:
+       case DW_OP_call4:
+       case DW_OP_GNU_parameter_ref:
+         if (op == DW_OP_call2)
+           addr = read_16 (ptr);
+         else
+           addr = read_32 (ptr);
+         ref = off_htab_lookup (die->die_cu->cu_offset + addr);
+         if (ref == NULL)
+           {
+             error (0, 0, "%s: Couldn't find DIE referenced by DW_OP_%d",
+                    dso->filename, op);
+             return 1;
+           }
+         if (op == DW_OP_call2)
+           ref->die_op_call2_referenced = 1;
+         if (ref->die_ck_state == CK_KNOWN)
+           {
+             ref->die_ck_state = CK_BAD;
+             while (ref->die_parent != NULL
+                    && ref->die_parent->die_ck_state == CK_KNOWN)
+               {
+                 ref = ref->die_parent;
+                 ref->die_ck_state = CK_BAD;
+               }
+           }
+         else
+           ref->die_ck_state = CK_BAD;
+         die->die_ck_state = CK_BAD;
+         if (need_adjust)
+           *need_adjust = true;
+         break;
+       case DW_OP_const4u:
+       case DW_OP_const4s:
+         ptr += 4;
+         break;
+       case DW_OP_call_ref:
+       case DW_OP_GNU_implicit_pointer:
+         addr = read_size (ptr, die->die_cu->cu_version == 2 ? ptr_size : 4);
+         if (die->die_cu->cu_version == 2)
+           ptr += ptr_size;
+         else
+           ptr += 4;
+         ref = off_htab_lookup (addr);
+         if (ref == NULL)
+           {
+             error (0, 0, "%s: Couldn't find DIE referenced by DW_OP_%d",
+                    dso->filename, op);
+             return 1;
+           }
+         die->die_ck_state = CK_BAD;
+         if (need_adjust)
+           *need_adjust = true;
+         if (op == DW_OP_GNU_implicit_pointer)
+           read_uleb128 (ptr);
+         break;
+       case DW_OP_const8u:
+       case DW_OP_const8s:
+         ptr += 8;
+         break;
+       case DW_OP_constu:
+       case DW_OP_plus_uconst:
+       case DW_OP_regx:
+       case DW_OP_piece:
+       case DW_OP_consts:
+       case DW_OP_breg0 ... DW_OP_breg31:
+       case DW_OP_fbreg:
+         read_uleb128 (ptr);
+         break;
+       case DW_OP_bregx:
+       case DW_OP_bit_piece:
+         read_uleb128 (ptr);
+         read_uleb128 (ptr);
+         break;
+       case DW_OP_implicit_value:
+         {
+           uint32_t leni = read_uleb128 (ptr);
+           ptr += leni;
+         }
+         break;
+       case DW_OP_GNU_entry_value:
+         {
+           uint32_t leni = read_uleb128 (ptr);
+           if ((end - ptr) < leni)
+             {
+               error (0, 0, "%s: DWARF DW_OP_GNU_entry_value with too large"
+                      " length", dso->filename);
+               return 1;
+             }
+           if (read_exprloc (dso, die, ptr, leni, need_adjust))
+             return 1;
+           ptr += leni;
+         }
+         break;
+       case DW_OP_GNU_convert:
+       case DW_OP_GNU_reinterpret:
+         addr = read_uleb128 (ptr);
+         if (addr == 0)
+           break;
+         goto typed_dwarf;
+       case DW_OP_GNU_regval_type:
+         read_uleb128 (ptr);
+         addr = read_uleb128 (ptr);
+         goto typed_dwarf;
+       case DW_OP_GNU_const_type:
+         addr = read_uleb128 (ptr);
+         ptr += *ptr + 1;
+         goto typed_dwarf;
+       case DW_OP_GNU_deref_type:
+         ++ptr;
+         addr = read_uleb128 (ptr);
+       typed_dwarf:
+         ref = off_htab_lookup (die->die_cu->cu_offset + addr);
+         if (ref == NULL)
+           {
+             error (0, 0, "%s: Couldn't find DIE referenced by DW_OP_%d",
+                    dso->filename, op);
+             return 1;
+           }
+         ref->die_op_type_referenced = 1;
+         die->die_ck_state = CK_BAD;
+         if (need_adjust)
+           *need_adjust = true;
+         break;
+       default:
+         error (0, 0, "%s: Unknown DWARF DW_OP_%d", dso->filename, op);
+         return 1;
+       }
+    }
+  if (die->die_ck_state != CK_BAD)
+    die->u.p1.die_hash = iterative_hash (end - len, len, die->u.p1.die_hash);
+  return 0;
+}
+
+/* Structure recording a portion of .debug_loc section that will need
+   adjusting.  */
+struct debug_loc_adjust
+{
+  /* Starting offset in .debug_loc that needs adjusting.  */
+  unsigned int start_offset;
+  /* End offset.  This is used for hashing, as in theory some DIE
+     might be referencing a middle of a .debug_loc sequence (the address
+     part of it) referenced by a different DIE.  */
+  unsigned int end_offset;
+  /* Owning CU.  We give up if the same .debug_loc part that needs adjusting
+     is owned by more than one CU.  */
+  struct dw_cu *cu;
+};
+
+/* Hash table and obstack for recording .debug_loc adjustment ranges.  */
+static htab_t loc_htab;
+static struct obstack loc_ob;
+
+/* Hash function for loc_htab.  */
+static hashval_t
+loc_hash (const void *p)
+{
+  struct debug_loc_adjust *a = (struct debug_loc_adjust *)p;
+
+  return a->end_offset;
+}
+
+/* Equality function for loc_htab.  */
+static int
+loc_eq (const void *p, const void *q)
+{
+  struct debug_loc_adjust *t1 = (struct debug_loc_adjust *)p;
+  struct debug_loc_adjust *t2 = (struct debug_loc_adjust *)q;
+
+  return t1->end_offset == t2->end_offset;
+}
+
+/* Parse .debug_loc portion starting at OFFSET, referenced by
+   DIE.  Call read_exprloc on each of the DWARF expressions
+   contained in it.  */
+static int
+read_loclist (DSO *dso, dw_die_ref die, GElf_Addr offset)
+{
+  unsigned char *ptr, *endsec;
+  GElf_Addr low, high;
+  size_t len;
+  bool need_adjust = false;
+
+  die->die_ck_state = CK_BAD;
+  ptr = debug_sections[DEBUG_LOC].data;
+  if (ptr == NULL)
+    {
+      error (0, 0, "%s: loclistptr attribute, yet no .debug_loc section",
+            dso->filename);
+      return 1;
+    }
+  if (offset >= debug_sections[DEBUG_LOC].size)
+    {
+      error (0, 0,
+            "%s: loclistptr offset %Ld outside of .debug_loc section",
+            dso->filename, (long long) offset);
+      return 1;
+    }
+  endsec = ptr + debug_sections[DEBUG_LOC].size;
+  ptr += offset;
+  while (ptr < endsec)
+    {
+      low = read_size (ptr, ptr_size);
+      high = read_size (ptr + ptr_size, ptr_size);
+      ptr += 2 * ptr_size;
+      if (low == 0 && high == 0)
+       break;
+
+      if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff))
+       continue;
+
+      len = read_16 (ptr);
+      assert (ptr + len <= endsec);
+
+      if (read_exprloc (dso, die, ptr, len, &need_adjust))
+       return 1;
+
+      ptr += len;
+    }
+
+  if (need_adjust)
+    {
+      struct debug_loc_adjust adj, *a;
+      void **slot;
+
+      adj.start_offset = offset;
+      adj.end_offset = ptr - debug_sections[DEBUG_LOC].data;
+      adj.cu = die->die_cu;
+      if (loc_htab == NULL)
+       {
+         loc_htab = htab_try_create (50, loc_hash, loc_eq, NULL);
+         if (loc_htab == NULL)
+           {
+           no_memory:
+             error (0, ENOMEM, "%s: can't record .debug_loc adjustments",
+                    dso->filename);
+             return 1;
+           }
+         obstack_init (&loc_ob);
+       }
+      slot = htab_find_slot_with_hash (loc_htab, &adj, adj.end_offset, INSERT);
+      if (slot == NULL)
+       goto no_memory;
+      if (*slot == NULL)
+       {
+         a = obstack_alloc (&loc_ob, sizeof (*a));
+         *a = adj;
+         *slot = (void *) a;
+       }
+      else if (((struct debug_loc_adjust *)*slot)->cu != adj.cu)
+       {
+         error (0, 0, "%s: can't adjust .debug_loc section because multiple "
+                      "CUs refer to it", dso->filename);
+         return 1;
+       }
+      else if (((struct debug_loc_adjust *)*slot)->start_offset > offset)
+       ((struct debug_loc_adjust *)*slot)->start_offset = offset;
+    }
+
+  return 0;
+}
+
+/* This function computes u.p1.die_hash and die_ck_state of DIE.
+   u.p1.die_hash is an iterative hash of the die_tag, for all its attributes
+   except DW_AT_sibling the attribute code, for non-reference class
+   attributes or DW_FORM_ref_addr attributes the value of the attribute
+   (magic for DW_AT_*_file), for reference class attributes that point
+   into the subtree of TOP_DIE ref->u.p1.die_enter - top_die->u.p1.die_enter
+   (note, other references are intentionally ignored here) and hashes
+   of all its children.  die_ck_state is set to CK_BAD if the die is
+   unsuitable for moving into a partial unit (contains code references
+   or other reasons).  TOP_DIE is initially NULL when DW_TAG_*_unit or
+   die_named_namespace dies are walked.  */
+static int
+checksum_die (DSO *dso, dw_die_ref top_die, dw_die_ref die)
+{
+  unsigned short s;
+  struct abbrev_tag *t;
+  unsigned int i;
+  unsigned char *ptr;
+  dw_die_ref child;
+
+  switch (die->die_ck_state)
+    {
+    case CK_UNKNOWN:
+      break;
+    case CK_KNOWN:
+    case CK_BAD:
+      return 0;
+    case CK_BEING_COMPUTED:
+      die->die_ck_state = CK_BAD;
+      return 0;
+    }
+  die->die_ck_state = CK_BEING_COMPUTED;
+  die->u.p1.die_hash = 0;
+  if (die->die_tag == DW_TAG_compile_unit
+      || die->die_tag == DW_TAG_partial_unit
+      || die->die_tag == DW_TAG_namespace
+      || die->die_tag == DW_TAG_module)
+    die->die_ck_state = CK_BAD;
+  t = die->die_abbrev;
+  ptr = debug_sections[DEBUG_INFO].data + die->die_offset;
+  read_uleb128 (ptr);
+  s = die->die_tag;
+  die->u.p1.die_hash = iterative_hash_object (s, die->u.p1.die_hash);
+  for (i = 0; i < t->nattr; ++i)
+    {
+      uint32_t form = t->attr[i].form;
+      size_t len = 0;
+      unsigned char *old_ptr;
+      bool handled = false;
+      uint64_t value;
+
+      while (form == DW_FORM_indirect)
+       form = read_uleb128 (ptr);
+      old_ptr = ptr;
+
+      switch (t->attr[i].attr)
+       {
+       /* Ignore DW_AT_sibling attribute.  */
+       case DW_AT_sibling:
+         handled = true;
+         break;
+       /* These attributes reference code, prevent moving
+          DIEs with them.  */
+       case DW_AT_low_pc:
+       case DW_AT_high_pc:
+       case DW_AT_entry_pc:
+       case DW_AT_ranges:
+         die->die_ck_state = CK_BAD;
+         break;
+       case DW_AT_location:
+       case DW_AT_string_length:
+       case DW_AT_return_addr:
+       case DW_AT_data_member_location:
+       case DW_AT_frame_base:
+       case DW_AT_segment:
+       case DW_AT_static_link:
+       case DW_AT_use_location:
+       case DW_AT_vtable_elem_location:
+         if ((die->die_cu->cu_version < 4 && form == DW_FORM_data4)
+              || form == DW_FORM_sec_offset)
+           {
+             if (read_loclist (dso, die, read_32 (ptr)))
+               return 1;
+             ptr = old_ptr;
+             break;
+           }
+         else if (die->die_cu->cu_version < 4 && form == DW_FORM_data8)
+           {
+             if (read_loclist (dso, die, read_64 (ptr)))
+               return 1;
+             ptr = old_ptr;
+             break;
+           }
+         break;
+       case DW_AT_decl_file:
+       case DW_AT_call_file:
+         switch (form)
+           {
+           case DW_FORM_data1: value = read_8 (ptr); handled = true; break;
+           case DW_FORM_data2: value = read_16 (ptr); handled = true; break;
+           case DW_FORM_data4: value = read_32 (ptr); handled = true; break;
+           case DW_FORM_data8: value = read_64 (ptr); handled = true; break;
+           case DW_FORM_udata:
+             value = read_uleb128 (ptr); handled = true; break;
+           default:
+             error (0, 0, "%s: Unhandled DW_FORM_%d for DW_AT_%d",
+                    dso->filename, form, t->attr[i].attr);
+             return 1;
+           }
+         if (handled)
+           {
+             unsigned char *new_ptr = ptr;
+             ptr = old_ptr;
+             if (value > die->die_cu->cu_nfiles)
+               {
+                 error (0, 0, "%s: Invalid DW_AT_%d file number %d",
+                        dso->filename, t->attr[i].attr, (int) value);
+                 return 1;
+               }
+             if (value == 0)
+               handled = false;
+             else if (!IGNORE_LOCUS && die->die_ck_state != CK_BAD)
+               {
+                 struct dw_file *cu_file = &die->die_cu->cu_files[value - 1];
+                 size_t file_len = strlen (cu_file->file);
+                 s = t->attr[i].attr;
+                 die->u.p1.die_hash
+                   = iterative_hash_object (s, die->u.p1.die_hash);
+                 die->u.p1.die_hash
+                   = iterative_hash_object (cu_file->time,
+                                            die->u.p1.die_hash);
+                 die->u.p1.die_hash
+                   = iterative_hash_object (cu_file->size,
+                                            die->u.p1.die_hash);
+                 die->u.p1.die_hash
+                   = iterative_hash (cu_file->file, file_len + 1,
+                                     die->u.p1.die_hash);
+                 if (cu_file->dir)
+                   die->u.p1.die_hash
+                     = iterative_hash (cu_file->dir,
+                                       strlen (cu_file->dir) + 1,
+                                       die->u.p1.die_hash);
+                 /* Ignore DW_AT_comp_dir for DW_AT_*_file <built-in>
+                    etc. if immediately followed by DW_AT_*_line 0.  */
+                 else if (cu_file->file[0] == '<'
+                          && cu_file->file[file_len - 1] == '>'
+                          && strchr (cu_file->file, '/') == NULL
+                          && i + 1 < t->nattr
+                          && t->attr[i + 1].attr
+                             == (t->attr[i].attr == DW_AT_decl_file
+                                 ? DW_AT_decl_line : DW_AT_call_line)
+                          && t->attr[i + 1].form == DW_FORM_data1
+                          && *new_ptr == 0)
+                   break;
+                 if (die->die_cu->cu_comp_dir
+                     && (cu_file->dir ? cu_file->dir[0]
+                                      : cu_file->file[0]) != '/')
+                   die->u.p1.die_hash
+                     = iterative_hash (die->die_cu->cu_comp_dir,
+                                       strlen (die->die_cu->cu_comp_dir) + 1,
+                                       die->u.p1.die_hash);
+               }
+           }
+         break;
+       case DW_AT_decl_line:
+       case DW_AT_decl_column:
+       case DW_AT_call_line:
+       case DW_AT_call_column:
+         if (IGNORE_LOCUS)
+           handled = true;
+         break;
+       default:
+         break;
+       }
+
+      switch (form)
+       {
+       case DW_FORM_ref_addr:
+         ptr += die->die_cu->cu_version == 2 ? ptr_size : 4;
+         break;
+       case DW_FORM_addr:
+         ptr += ptr_size;
+         break;
+       case DW_FORM_flag_present:
+         break;
+       case DW_FORM_flag:
+       case DW_FORM_data1:
+         ++ptr;
+         break;
+       case DW_FORM_data2:
+         ptr += 2;
+         break;
+       case DW_FORM_data4:
+       case DW_FORM_sec_offset:
+       case DW_FORM_strp:
+         ptr += 4;
+         break;
+       case DW_FORM_data8:
+       case DW_FORM_ref_sig8:
+         ptr += 8;
+         break;
+       case DW_FORM_sdata:
+       case DW_FORM_udata:
+         read_uleb128 (ptr);
+         break;
+       case DW_FORM_ref_udata:
+       case DW_FORM_ref1:
+       case DW_FORM_ref2:
+       case DW_FORM_ref4:
+       case DW_FORM_ref8:
+         switch (form)
+           {
+           case DW_FORM_ref_udata: value = read_uleb128 (ptr); break;
+           case DW_FORM_ref1: value = read_8 (ptr); break;
+           case DW_FORM_ref2: value = read_16 (ptr); break;
+           case DW_FORM_ref4: value = read_32 (ptr); break;
+           case DW_FORM_ref8: value = read_64 (ptr); break;
+           default: abort ();
+           }
+         if (!handled)
+           {
+             dw_die_ref ref
+               = off_htab_lookup (die->die_cu->cu_offset + value);
+             if (ref == NULL)
+               {
+                 error (0, 0, "%s: Couldn't find DIE referenced by DW_AT_%d",
+                        dso->filename, t->attr[i].attr);
+                 return 1;
+               }
+             if (die->die_ck_state != CK_BAD)
+               {
+                 s = t->attr[i].attr;
+                 die->u.p1.die_hash
+                   = iterative_hash_object (s, die->u.p1.die_hash);
+               }
+             if (top_die
+                 && ref->u.p1.die_enter >= top_die->u.p1.die_enter
+                 && ref->u.p1.die_exit <= top_die->u.p1.die_exit)
+               {
+                 if (die->die_ck_state != CK_BAD)
+                   {
+                     unsigned int val
+                       = ref->u.p1.die_enter - top_die->u.p1.die_enter;
+                     die->u.p1.die_hash
+                       = iterative_hash_object (val, die->u.p1.die_hash);
+                   }
+               }
+             handled = true;
+           }
+         break;
+       case DW_FORM_string:
+         ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+         break;
+       case DW_FORM_indirect:
+         abort ();
+       case DW_FORM_block1:
+         len = *ptr++;
+         break;
+       case DW_FORM_block2:
+         len = read_16 (ptr);
+         form = DW_FORM_block1;
+         break;
+       case DW_FORM_block4:
+         len = read_32 (ptr);
+         form = DW_FORM_block1;
+         break;
+       case DW_FORM_block:
+         len = read_uleb128 (ptr);
+         form = DW_FORM_block1;
+         break;
+       case DW_FORM_exprloc:
+         len = read_uleb128 (ptr);
+         break;
+       default:
+         abort ();
+       }
+
+      if (form == DW_FORM_block1)
+       {
+         switch (t->attr[i].attr)
+           {
+           case DW_AT_frame_base:
+           case DW_AT_location:
+           case DW_AT_data_member_location:
+           case DW_AT_vtable_elem_location:
+           case DW_AT_byte_size:
+           case DW_AT_bit_offset:
+           case DW_AT_bit_size:
+           case DW_AT_string_length:
+           case DW_AT_lower_bound:
+           case DW_AT_return_addr:
+           case DW_AT_bit_stride:
+           case DW_AT_upper_bound:
+           case DW_AT_count:
+           case DW_AT_segment:
+           case DW_AT_static_link:
+           case DW_AT_use_location:
+           case DW_AT_allocated:
+           case DW_AT_associated:
+           case DW_AT_data_location:
+           case DW_AT_byte_stride:
+           case DW_AT_GNU_call_site_value:
+           case DW_AT_GNU_call_site_data_value:
+           case DW_AT_GNU_call_site_target:
+           case DW_AT_GNU_call_site_target_clobbered:
+             if (die->die_ck_state != CK_BAD)
+               {
+                 s = t->attr[i].attr;
+                 die->u.p1.die_hash
+                   = iterative_hash_object (s, die->u.p1.die_hash);
+               }
+             if (read_exprloc (dso, die, ptr, len, NULL))
+               return 1;
+             handled = true;
+           default:
+             break;
+           }
+         ptr += len;
+       }
+      else if (form == DW_FORM_exprloc)
+       {
+         if (die->die_ck_state != CK_BAD)
+           {
+             s = t->attr[i].attr;
+             die->u.p1.die_hash
+               = iterative_hash_object (s, die->u.p1.die_hash);
+           }
+         if (read_exprloc (dso, die, ptr, len, NULL))
+           return 1;
+         handled = true;
+         ptr += len;
+       }
+      if (!handled && die->die_ck_state != CK_BAD)
+       {
+         s = t->attr[i].attr;
+         die->u.p1.die_hash = iterative_hash_object (s, die->u.p1.die_hash);
+         die->u.p1.die_hash
+           = iterative_hash (old_ptr, ptr - old_ptr, die->u.p1.die_hash);
+       }
+    }
+
+  for (child = die->die_child; child; child = child->die_sib)
+    if (checksum_die (dso, top_die ? top_die
+                                  : child->die_named_namespace
+                                    ? NULL : child, child))
+      return 1;
+    else if (die->die_ck_state != CK_BAD)
+      {
+       if (child->die_ck_state == CK_KNOWN)
+         die->u.p1.die_hash
+           = iterative_hash_object (child->u.p1.die_hash, die->u.p1.die_hash);
+       else
+         die->die_ck_state = CK_BAD;
+      }
+  if (die->die_ck_state == CK_BEING_COMPUTED)
+    die->die_ck_state = CK_KNOWN;
+
+  return 0;
+}
+
+/* Helper function for checksum_ref_die to sort DIE pointers
+   by increasing u.p1.die_hash.  */
+static int
+checksum_ref_die_cmp (const void *p, const void *q)
+{
+  dw_die_ref die1 = *(dw_die_ref *)p;
+  dw_die_ref die2 = *(dw_die_ref *)q;
+  if (die1->u.p1.die_hash < die2->u.p1.die_hash)
+    return -1;
+  if (die1->u.p1.die_hash > die2->u.p1.die_hash)
+    return 1;
+  /* The rest is just to keep the sort stable.  If there is more than
+     one DIE with the same hash, we don't consider any of them as suitable
+     starting point for the walk.  */
+  if (die1->die_offset < die2->die_offset)
+    return -1;
+  if (die1->die_offset > die2->die_offset)
+    return 1;
+  return 0;
+}
+
+/* This function is the second phase of hash computation, which computes
+   u.p1.die_ref_hash after u.p1.die_hash has been computed.
+   u.p1.die_ref_hash is an iterative hash of the references (other than
+   those checksummed already into u.p1.die_hash by checksum_die).
+   u.p1.die_ref_hash is only computed for the toplevel DIEs, i.e. children
+   of DW_TAG_*_unit or die_named_namespace DIEs.  So, in the graph
+   containing DIEs as nodes and parent<->child and referrer<->referree edges
+   we virtually coalesce all children of toplevel DIEs into the
+   corresponding toplevel DIE ultimate parent node.  The function has 4
+   modes of operation:
+
+   The first one is when TOP_DIE, SECOND_IDX and SECOND_HASH are all NULL,
+   this is when we walk through DW_TAG_*_unit and die_named_namespace DIEs
+   to reach their children.
+
+   The second mode of operation is with TOP_DIE != NULL and both SECOND_IDX
+   and SECOND_HASH NULL.  In this mode we optimistically assume there are no
+   cycles in the graph, first hash into TOP_DIE's u.p1.die_ref_hash its
+   u.p1.die_hash, push the TOP_DIE into a vector (in OB obstack), for each
+   reference if the referree isn't already toplevel DIE find its
+   (grand)*parent that is a toplevel DIE, recurse on that and if it computed
+   the referree toplevel DIE's u.p1.die_ref_hash (i.e. no cycle),
+   iteratively hash in the referree toplevel DIE's u.p1.die_ref_hash (and,
+   if referree isn't toplevel, before that also its relative position in the
+   subtree).  When existing, remove the TOP_DIE from the vector and set
+   die_ref_hash_computed to note that it is computed and doesn't have to be
+   computed again.  If there are no cycles, the return value of the function
+   is 0.  If a cycle is found, things are more complicated.  We can't just
+   not walk into DIEs we've already seen and compute u.p1.die_ref_hash for
+   toplevel DIEs on the cycle(s), because in different CUs matching cycles
+   might be starting computation of the hash from different nodes on the
+   cycle (the order of children of DW_TAG_*_unit or DW_TAG_namespace is
+   usually not significant in DWARF).  So, if a cycle is found, the return
+   value is a minimum of the die_ref_seen indexes (positions in the
+   vector); at that point it makes no sense to further compute
+   u.p1.die_ref_hash of the toplevel DIEs on the cycle, but for references
+   to acyclic subgraphs we still continue computing their u.p1.die_ref_hash.
+   For DIEs on the cycle(s) pointers to them aren't immediately removed from
+   the vector and everything is handled only after reaching the TOP_DIE with
+   die_ref_seen equal to the minimum vector index (i.e. the first of
+   the DIEs on the cycle(s) we've seen).  At this point in the vector
+   starting with that index should be a list of DIEs on the cycle, and all
+   references to (toplevel) DIEs not on that list from those DIEs should
+   have die_ref_hash_computed already set.  If the cycle has matches in
+   different CUs, we should have the same set of u.p1.die_hash values in the
+   list in between all those CUs, but the order might be different.  At this
+   point we try to find a DIE from which to start walk using third mode of
+   operation of this function.  We can't base that decision on e.g.
+   die_offset, as it may be not just different between the CUs, but the
+   matching DIEs might be in different relative orders.  So we look if there
+   is just a single DIE with lowest u.p1.die_hash value and use that in that
+   case, (and if not, look if there is just a single DIE with second lowest
+   u.p1.die_hash and so on up to 20th).  If none is found, the more
+   expensive 4th mode of operation is used instead.
+
+   The third mode of operation is when TOP_DIE, SECOND_IDX and SECOND_HASH
+   are all non-NULL.  This mode is used when the initial TOP_DIE is
+   a uniquely chosen DIE on the cycle (same in each CU that has
+   matching cycle).  In this mode into each TOP_DIE's u.p1.die_ref_hash
+   we hash in *SECOND_IDX (counter when in that walk the DIE has been
+   seen first) and relative referree positions in subtree, and hash in
+   referree toplevel u.p1.die_ref_hash into *SECOND_HASH, and finally
+   when the walk finishes, the caller will compute the final
+   u.p1.die_ref_hash for DIEs on the cycle(s) from their intermediate
+   u.p1.die_ref_hash and *SECOND_HASH.
+
+   The last mode of operation is when TOP_DIE and SECOND_HASH
+   are non-NULL, but SECOND_IDX is NULL.  This mode is used when no
+   suitable DIE from which to start walking the cycle has been discovered.
+   In that case we for each DIE on the cycle walk everything that hasn't
+   die_ref_hash_computed yet, for DIEs seen that have die_ref_hash_computed
+   hash in their u.p1.die_ref_hash, otherwise (DIEs on the current cycle(s))
+   hash in their u.p1.die_hash.  */
+static unsigned int
+checksum_ref_die (dw_die_ref top_die, dw_die_ref die, unsigned int *second_idx,
+                 hashval_t *second_hash)
+{
+  struct abbrev_tag *t;
+  unsigned int i, ret = 0;
+  unsigned char *ptr;
+  dw_die_ref child;
+
+  if (top_die == die)
+    {
+      if (die->die_ref_hash_computed)
+       return 0;
+      if (die->die_ck_state != CK_KNOWN)
+       return 0;
+      if (die->die_ref_seen)
+       return second_hash != NULL ? 0 : die->die_ref_seen;
+      if (second_hash != NULL)
+       {
+         die->die_ref_seen = 1;
+         if (second_idx != NULL)
+           die->u.p1.die_ref_hash
+             = iterative_hash_object (*second_idx, die->u.p1.die_hash);
+       }
+      else
+       {
+         die->die_ref_seen
+           = obstack_object_size (&ob) / sizeof (void *) + 1;
+         obstack_ptr_grow (&ob, die);
+         die->u.p1.die_ref_hash = die->u.p1.die_hash;
+       }
+    }
+  else
+    assert (top_die == NULL || die->die_ck_state == CK_KNOWN);
+  t = die->die_abbrev;
+  for (i = 0; i < t->nattr; ++i)
+    if (t->attr[i].attr != DW_AT_sibling)
+      switch (t->attr[i].form)
+       {
+       case DW_FORM_ref_udata:
+       case DW_FORM_ref1:
+       case DW_FORM_ref2:
+       case DW_FORM_ref4:
+       case DW_FORM_ref8:
+       case DW_FORM_indirect:
+         i = -2U;
+         break;
+       }
+  if (i == -1U)
+    {
+      ptr = debug_sections[DEBUG_INFO].data + die->die_offset;
+      read_uleb128 (ptr);
+      for (i = 0; i < t->nattr; ++i)
+       {
+         uint32_t form = t->attr[i].form;
+         size_t len = 0;
+         uint64_t value;
+         dw_die_ref ref, reft;
+
+         while (form == DW_FORM_indirect)
+           form = read_uleb128 (ptr);
+
+         switch (form)
+           {
+           case DW_FORM_ref_addr:
+             ptr += die->die_cu->cu_version == 2 ? ptr_size : 4;
+             break;
+           case DW_FORM_addr:
+             ptr += ptr_size;
+             break;
+           case DW_FORM_flag_present:
+             break;
+           case DW_FORM_flag:
+           case DW_FORM_data1:
+             ++ptr;
+             break;
+           case DW_FORM_data2:
+             ptr += 2;
+             break;
+           case DW_FORM_data4:
+           case DW_FORM_sec_offset:
+           case DW_FORM_strp:
+             ptr += 4;
+             break;
+           case DW_FORM_data8:
+           case DW_FORM_ref_sig8:
+             ptr += 8;
+             break;
+           case DW_FORM_sdata:
+           case DW_FORM_udata:
+             read_uleb128 (ptr);
+             break;
+           case DW_FORM_ref_udata:
+           case DW_FORM_ref1:
+           case DW_FORM_ref2:
+           case DW_FORM_ref4:
+           case DW_FORM_ref8:
+             switch (form)
+               {
+               case DW_FORM_ref_udata: value = read_uleb128 (ptr); break;
+               case DW_FORM_ref1: value = read_8 (ptr); break;
+               case DW_FORM_ref2: value = read_16 (ptr); break;
+               case DW_FORM_ref4: value = read_32 (ptr); break;
+               case DW_FORM_ref8: value = read_64 (ptr); break;
+               default: abort ();
+               }
+             if (t->attr[i].attr == DW_AT_sibling || top_die == NULL)
+               break;
+             ref = off_htab_lookup (die->die_cu->cu_offset + value);
+             if (ref->u.p1.die_enter >= top_die->u.p1.die_enter
+                 && ref->u.p1.die_exit <= top_die->u.p1.die_exit)
+               break;
+             reft = ref;
+             while (reft->die_parent != NULL
+                    && reft->die_parent->die_tag != DW_TAG_compile_unit
+                    && reft->die_parent->die_tag != DW_TAG_partial_unit
+                    && !reft->die_parent->die_named_namespace)
+               reft = reft->die_parent;
+             if (reft->die_ck_state != CK_KNOWN || reft->die_parent == NULL)
+               top_die->die_ck_state = CK_BAD;
+             else
+               {
+                 unsigned int r = checksum_ref_die (reft, reft, second_idx,
+                                                    second_hash);
+                 if (ret == 0 || (r && r < ret))
+                   ret = r;
+                 if (reft->die_ck_state != CK_KNOWN)
+                   top_die->die_ck_state = CK_BAD;
+               }
+             if (top_die->die_ck_state == CK_BAD)
+               {
+                 if (top_die != die)
+                   return ret;
+                 i = t->nattr - 1;
+                 break;
+               }
+             if (ret)
+               break;
+             if (reft != ref)
+               {
+                 unsigned int val
+                   = ref->u.p1.die_enter - reft->u.p1.die_enter;
+                 if (second_hash != NULL && second_idx == NULL)
+                   *second_hash
+                     = iterative_hash_object (val, *second_hash);
+                 else
+                   top_die->u.p1.die_ref_hash
+                     = iterative_hash_object (val,
+                                              top_die->u.p1.die_ref_hash);
+               }
+             if (second_hash)
+               {
+                 if (second_idx == NULL && !reft->die_ref_hash_computed)
+                   *second_hash
+                     = iterative_hash_object (reft->u.p1.die_hash,
+                                              *second_hash);
+                 else
+                   *second_hash
+                     = iterative_hash_object (reft->u.p1.die_ref_hash,
+                                              *second_hash);
+               }
+             else
+               top_die->u.p1.die_ref_hash
+                 = iterative_hash_object (reft->u.p1.die_ref_hash,
+                                          top_die->u.p1.die_ref_hash);
+             break;
+           case DW_FORM_string:
+             ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+             break;
+           case DW_FORM_indirect:
+             abort ();
+           case DW_FORM_block1:
+             len = *ptr++;
+             break;
+           case DW_FORM_block2:
+             len = read_16 (ptr);
+             form = DW_FORM_block1;
+             break;
+           case DW_FORM_block4:
+             len = read_32 (ptr);
+             form = DW_FORM_block1;
+             break;
+           case DW_FORM_block:
+           case DW_FORM_exprloc:
+             len = read_uleb128 (ptr);
+             form = DW_FORM_block1;
+             break;
+           default:
+             abort ();
+           }
+
+         if (form == DW_FORM_block1)
+           ptr += len;
+       }
+    }
+
+  if (top_die == NULL || top_die->die_ck_state != CK_BAD)
+    for (child = die->die_child; child; child = child->die_sib)
+      {
+       unsigned int r
+         = checksum_ref_die (top_die ? top_die
+                             : child->die_named_namespace
+                             ? NULL : child, child,
+                             second_idx, second_hash);
+       if (top_die == NULL)
+         assert (r == 0 && obstack_object_size (&ob) == 0);
+
+       if (ret == 0 || (r && r < ret))
+         ret = r;
+       if (top_die && top_die->die_ck_state == CK_BAD)
+         break;
+      }
+
+  if (top_die == die)
+    {
+      if (ret == 0)
+       {
+         if (second_hash != NULL)
+           return 0;
+         die->die_ref_seen = 0;
+         die->die_ref_hash_computed = 1;
+         obstack_blank_fast (&ob, -(int) sizeof (void *));
+         return 0;
+       }
+      assert (ret <= die->die_ref_seen);
+      if (ret == die->die_ref_seen)
+       {
+         unsigned int first = die->die_ref_seen - 1;
+         dw_die_ref *arr;
+         unsigned int count
+           = obstack_object_size (&ob) / sizeof (void *) - first;
+         unsigned int idx, minidx;
+         hashval_t ref_hash = 0;
+         bool bad = false;
+
+         arr = (dw_die_ref *) obstack_base (&ob) + first;
+         for (i = 0; i < count; i++)
+           {
+             arr[i]->die_ref_seen = 0;
+             if (arr[i]->die_ck_state == CK_BAD)
+               bad = true;
+           }
+         if (bad)
+           {
+             for (i = 0; i < count; i++)
+               arr[i]->die_ck_state = CK_BAD;
+             obstack_blank_fast (&ob, -(int) (count * sizeof (void *)));
+             return 0;
+           }
+         /* Find the DIE in the array with the smallest u.p1.die_hash.  */
+         for (i = 0, minidx = -1U, bad = true; i < count; i++)
+           {
+             if (minidx == -1U
+                 || arr[i]->u.p1.die_hash < arr[minidx]->u.p1.die_hash)
+               {
+                 minidx = i;
+                 bad = false;
+               }
+             else if (arr[i]->u.p1.die_hash == arr[minidx]->u.p1.die_hash)
+               bad = true;
+           }
+         if (bad)
+           {
+             unsigned int iter, limv;
+             /* If there is more than one smallest u.p1.die_hash,
+                look for second (up to 6th) smallest u.p1.die_hash
+                if there is just one of that value.  */
+             for (iter = 0; iter < 5; iter++)
+               {
+                 limv = arr[minidx]->u.p1.die_hash;
+                 for (i = 0, minidx = -1U, bad = true; i < count; i++)
+                   if (arr[i]->u.p1.die_hash <= limv)
+                     continue;
+                   else if (minidx == -1U
+                            || arr[i]->u.p1.die_hash
+                               < arr[minidx]->u.p1.die_hash)
+                     {
+                       minidx = i;
+                       bad = false;
+                     }
+                   else if (arr[i]->u.p1.die_hash
+                            == arr[minidx]->u.p1.die_hash)
+                     bad = true;
+                 if (minidx == -1U || !bad)
+                   break;
+               }
+             /* If all of 1st to 6th smallest u.p1.die_hash has more than
+                one DIE with that u.p1.die_hash, sort the array and find
+                the smallest u.p1.die_hash that only a single DIE has.  */
+             if (minidx != -1U && iter == 5)
+               {
+                 unsigned int j;
+                 qsort (arr, count, sizeof (void *), checksum_ref_die_cmp);
+                 for (i = 0, minidx = -1U; i < count; i = j)
+                   {
+                     if (i + 1 == count
+                         || arr[i + 1]->u.p1.die_hash
+                            != arr[i]->u.p1.die_hash)
+                       {
+                         minidx = i;
+                         break;
+                       }
+                     for (j = i + 1; j < count; j++)
+                       if (arr[j]->u.p1.die_hash != arr[i]->u.p1.die_hash)
+                         break;
+                   }
+               }
+           }
+         if (minidx != -1U)
+           {
+             idx = 0;
+             checksum_ref_die (arr[minidx], arr[minidx], &idx, &ref_hash);
+             assert (arr == (dw_die_ref *) obstack_base (&ob) + first);
+             for (i = 0; i < count; i++)
+               {
+                 arr[i]->u.p1.die_ref_hash
+                   = iterative_hash_object (arr[i]->u.p1.die_ref_hash,
+                                            ref_hash);
+                 arr[i]->die_ref_hash_computed = 1;
+                 arr[i]->die_ref_seen = 0;
+               }
+           }
+         else
+           {
+             /* If we get here, all u.p1.die_hash values in the arr array
+                are used by more than one day.  Do the more expensive
+                computation as fallback.  */
+             for (i = 0; i < count; i++)
+               {
+                 unsigned int j;
+                 arr[i]->u.p1.die_ref_hash = arr[i]->u.p1.die_hash;
+                 checksum_ref_die (arr[i], arr[i], NULL,
+                                   &arr[i]->u.p1.die_ref_hash);
+                 assert (arr == (dw_die_ref *) obstack_base (&ob) + first);
+                 for (j = 0; j < count; j++)
+                   arr[j]->die_ref_seen = 0;
+               }
+             for (i = 0; i < count; i++)
+               arr[i]->die_ref_hash_computed = 1;
+           }
+         obstack_blank_fast (&ob, -(int) (count * sizeof (void *)));
+         return 0;
+       }
+    }
+  return ret;
+}
+
+/* Hash function for dup_htab.  Both u.p1.die_hash and
+   u.p1.die_ref_hash are interesting.  */
+static hashval_t
+die_hash (const void *p)
+{
+  dw_die_ref die = (dw_die_ref) p;
+
+  return die->u.p1.die_hash ^ die->u.p1.die_ref_hash;
+}
+
+/* Return 1 if DIE1 and DIE2 match.  TOP_DIE1 and TOP_DIE2
+   is the corresponding ultimate parent with die_toplevel
+   set.  u.p1.die_hash and u.p1.die_ref_hash hashes should
+   hopefully ensure that in most cases this function actually
+   just verifies matching.  */
+static int
+die_eq_1 (dw_die_ref top_die1, dw_die_ref top_die2,
+         dw_die_ref die1, dw_die_ref die2)
+{
+  struct abbrev_tag *t1, *t2;
+  unsigned int i, j;
+  unsigned char *ptr1, *ptr2;
+  dw_die_ref ref1, ref2;
+  dw_die_ref child1, child2;
+  bool inside1, inside2;
+
+#define FAIL goto fail
+  if (die1 == die2 || die_safe_dup (die2) == die1)
+    return 1;
+  if (die1->u.p1.die_hash != die2->u.p1.die_hash
+      || die1->u.p1.die_ref_hash != die2->u.p1.die_ref_hash
+      || die1->die_tag != die2->die_tag
+      || die1->die_cu == die2->die_cu
+      || die1->u.p1.die_exit - die1->u.p1.die_enter
+        != die2->u.p1.die_exit - die2->u.p1.die_enter
+      || die_safe_dup (die2) != NULL
+      || die1->die_ck_state != CK_KNOWN
+      || die2->die_ck_state != CK_KNOWN
+      || die1->die_toplevel != die2->die_toplevel)
+    return 0;
+  assert (die1->die_parent != NULL
+         && die2->die_parent != NULL);
+
+  inside1 = die1->u.p1.die_enter >= top_die1->u.p1.die_enter
+           && die1->u.p1.die_exit <= top_die1->u.p1.die_exit;
+  inside2 = die2->u.p1.die_enter >= top_die2->u.p1.die_enter
+           && die2->u.p1.die_exit <= top_die2->u.p1.die_exit;
+  if (inside1 ^ inside2)
+    return 0;
+  if (inside1
+      && die1->u.p1.die_enter - top_die1->u.p1.die_enter
+        != die2->u.p1.die_enter - top_die2->u.p1.die_enter)
+    return 0;
+  if (!inside1)
+    {
+      for (ref1 = die1->die_parent, ref2 = die2->die_parent;
+          ref1 && ref2; ref1 = ref1->die_parent, ref2 = ref2->die_parent)
+       {
+         const char *name1, *name2;
+         if ((ref1->die_tag == DW_TAG_compile_unit
+              || ref1->die_tag == DW_TAG_partial_unit)
+             && (ref2->die_tag == DW_TAG_compile_unit
+                 || ref2->die_tag == DW_TAG_partial_unit))
+           break;
+         if (ref1->die_tag != ref2->die_tag)
+           return 0;
+         if (!ref1->die_named_namespace || !ref2->die_named_namespace)
+           return 0;
+         name1 = get_AT_string (ref1, DW_AT_name);
+         name2 = get_AT_string (ref2, DW_AT_name);
+         if (strcmp (name1, name2))
+           return 0;
+       }
+      if (ref1 == NULL || ref2 == NULL)
+       return 0;
+    }
+  t1 = die1->die_abbrev;
+  t2 = die2->die_abbrev;
+  ptr1 = debug_sections[DEBUG_INFO].data + die1->die_offset;
+  ptr2 = debug_sections[DEBUG_INFO].data + die2->die_offset;
+  read_uleb128 (ptr1);
+  read_uleb128 (ptr2);
+  i = 0;
+  j = 0;
+  if (die1->die_toplevel)
+    {
+      /* For each toplevel die seen, record optimistically
+        that we expect them to match, to avoid recursing
+        on it again.  If non-match is determined later,
+        die_eq wrapper undoes this (which is why the DIE
+        pointer is added to the vector).  */
+      die_dup (die2) = die1;
+      if (!die2->die_op_type_referenced)
+       die2->die_remove = 1;
+      die_nextdup (die2) = die_nextdup (die1);
+      die_nextdup (die1) = die2;
+      obstack_ptr_grow (&ob, die2);
+    }
+  while (1)
+    {
+      uint32_t form1, form2;
+      size_t len = 0;
+      unsigned char *old_ptr1;
+      unsigned char *old_ptr2;
+      uint64_t value1, value2;
+
+      while (i < t1->nattr && t1->attr[i].attr == DW_AT_sibling)
+       {
+         form1 = t1->attr[i].form;
+         while (form1 == DW_FORM_indirect)
+           form1 = read_uleb128 (ptr1);
+         switch (form1)
+           {
+           case DW_FORM_ref_udata: read_uleb128 (ptr1); break;
+           case DW_FORM_ref1: ptr1++; break;
+           case DW_FORM_ref2: read_16 (ptr1); break;
+           case DW_FORM_ref4: read_32 (ptr1); break;
+           case DW_FORM_ref8: read_64 (ptr1); break;
+           default: FAIL;
+           }
+         i++;
+       }
+      while (j < t2->nattr && t2->attr[j].attr == DW_AT_sibling)
+       {
+         form2 = t2->attr[j].form;
+         while (form2 == DW_FORM_indirect)
+           form2 = read_uleb128 (ptr2);
+         switch (form2)
+           {
+           case DW_FORM_ref_udata: read_uleb128 (ptr2); break;
+           case DW_FORM_ref1: ptr2++; break;
+           case DW_FORM_ref2: read_16 (ptr2); break;
+           case DW_FORM_ref4: read_32 (ptr2); break;
+           case DW_FORM_ref8: read_64 (ptr2); break;
+           default: FAIL;
+           }
+         j++;
+       }
+      if (i == t1->nattr)
+       {
+         if (j != t2->nattr)
+           FAIL;
+         break;
+       }
+      if (j == t2->nattr)
+       FAIL;
+
+      if (t1->attr[i].attr != t2->attr[j].attr)
+       FAIL;
+
+      form1 = t1->attr[i].form;
+      while (form1 == DW_FORM_indirect)
+       form1 = read_uleb128 (ptr1);
+      form2 = t2->attr[j].form;
+      while (form2 == DW_FORM_indirect)
+       form2 = read_uleb128 (ptr2);
+      old_ptr1 = ptr1;
+      old_ptr2 = ptr2;
+
+      switch (t1->attr[i].attr)
+       {
+       case DW_AT_sibling:
+       case DW_AT_low_pc:
+       case DW_AT_high_pc:
+       case DW_AT_entry_pc:
+       case DW_AT_ranges:
+         abort ();
+       case DW_AT_decl_file:
+       case DW_AT_call_file:
+         switch (form1)
+           {
+           case DW_FORM_data1: value1 = read_8 (ptr1); break;
+           case DW_FORM_data2: value1 = read_16 (ptr1); break;
+           case DW_FORM_data4: value1 = read_32 (ptr1); break;
+           case DW_FORM_data8: value1 = read_64 (ptr1); break;
+           case DW_FORM_udata: value1 = read_uleb128 (ptr1); break;
+           default: abort ();
+           }
+         switch (form2)
+           {
+           case DW_FORM_data1: value2 = read_8 (ptr2); break;
+           case DW_FORM_data2: value2 = read_16 (ptr2); break;
+           case DW_FORM_data4: value2 = read_32 (ptr2); break;
+           case DW_FORM_data8: value2 = read_64 (ptr2); break;
+           case DW_FORM_udata: value2 = read_uleb128 (ptr2); break;
+           default: abort ();
+           }
+         if (IGNORE_LOCUS)
+           {
+             i++;
+             j++;
+             continue;
+           }
+         if ((value1 == 0) ^ (value2 == 0))
+           FAIL;
+         if (value1 != 0)
+           {
+             struct dw_file *cu_file1
+               = &die1->die_cu->cu_files[value1 - 1];
+             struct dw_file *cu_file2
+               = &die2->die_cu->cu_files[value2 - 1];
+             unsigned int file_len;
+
+             if (cu_file1->time != cu_file2->time
+                 || cu_file1->size != cu_file2->size
+                 || strcmp (cu_file1->file, cu_file2->file))
+               FAIL;
+
+             file_len = strlen (cu_file1->file);
+             if (cu_file1->dir != NULL)
+               {
+                 if (cu_file2->dir == NULL
+                     || strcmp (cu_file1->dir, cu_file2->dir))
+                   FAIL;
+               }
+             else if (cu_file2->dir != NULL)
+               FAIL;
+             /* Ignore DW_AT_comp_dir for DW_AT_*_file <built-in>
+                etc. if immediately followed by DW_AT_*_line 0.  */
+             else if (cu_file1->file[0] == '<'
+                      && cu_file1->file[file_len - 1] == '>'
+                      && strchr (cu_file1->file, '/') == NULL
+                      && i + 1 < t1->nattr
+                      && j + 1 < t2->nattr
+                      && t1->attr[i + 1].attr
+                         == (t1->attr[i].attr == DW_AT_decl_file
+                             ? DW_AT_decl_line : DW_AT_call_line)
+                      && t1->attr[i + 1].form == DW_FORM_data1
+                      && t1->attr[i + 1].attr == t2->attr[j + 1].attr
+                      && t2->attr[j + 1].form == DW_FORM_data1
+                      && *ptr1 == 0
+                      && *ptr2 == 0)
+               {
+                 i++;
+                 j++;
+                 continue;
+               }
+
+             if ((cu_file1->dir ? cu_file1->dir[0] : cu_file1->file[0])
+                 != '/')
+               {
+                 if (die1->die_cu->cu_comp_dir != NULL)
+                   {
+                     if (die2->die_cu->cu_comp_dir == NULL
+                         || strcmp (die1->die_cu->cu_comp_dir,
+                                    die2->die_cu->cu_comp_dir))
+                       FAIL;
+                   }
+                 else if (die2->die_cu->cu_comp_dir != NULL)
+                   FAIL;
+               }
+           }
+         i++;
+         j++;
+         continue;
+       case DW_AT_decl_line:
+       case DW_AT_decl_column:
+       case DW_AT_call_line:
+       case DW_AT_call_column:
+         if (IGNORE_LOCUS)
+           old_ptr1 = NULL;
+         break;
+       default:
+         break;
+       }
+
+      if (form1 != form2)
+       FAIL;
+
+      switch (form1)
+       {
+       case DW_FORM_ref_addr:
+         ptr1 += die1->die_cu->cu_version == 2 ? ptr_size : 4;
+         ptr2 += die2->die_cu->cu_version == 2 ? ptr_size : 4;
+         break;
+       case DW_FORM_addr:
+         ptr1 += ptr_size;
+         ptr2 += ptr_size;
+         break;
+       case DW_FORM_flag_present:
+         break;
+       case DW_FORM_flag:
+       case DW_FORM_data1:
+         ++ptr1;
+         ++ptr2;
+         break;
+       case DW_FORM_data2:
+         ptr1 += 2;
+         ptr2 += 2;
+         break;
+       case DW_FORM_data4:
+       case DW_FORM_sec_offset:
+         ptr1 += 4;
+         ptr2 += 4;
+         break;
+       case DW_FORM_data8:
+       case DW_FORM_ref_sig8:
+         ptr1 += 8;
+         ptr2 += 8;
+         break;
+       case DW_FORM_sdata:
+       case DW_FORM_udata:
+         read_uleb128 (ptr1);
+         read_uleb128 (ptr2);
+         break;
+       case DW_FORM_strp:
+         ptr1 += 4;
+         ptr2 += 4;
+         break;
+       case DW_FORM_string:
+         ptr1 = (unsigned char *) strchr ((char *)ptr1, '\0') + 1;
+         ptr2 = (unsigned char *) strchr ((char *)ptr2, '\0') + 1;
+         break;
+       case DW_FORM_indirect:
+         abort ();
+       case DW_FORM_block1:
+         len = *ptr1++;
+         ptr1 += len;
+         len = *ptr2++;
+         ptr2 += len;
+         break;
+       case DW_FORM_block2:
+         len = read_16 (ptr1);
+         ptr1 += len;
+         len = read_16 (ptr2);
+         ptr2 += len;
+         break;
+       case DW_FORM_block4:
+         len = read_32 (ptr1);
+         ptr1 += len;
+         len = read_32 (ptr2);
+         ptr2 += len;
+         break;
+       case DW_FORM_block:
+       case DW_FORM_exprloc:
+         len = read_uleb128 (ptr1);
+         ptr1 += len;
+         len = read_uleb128 (ptr2);
+         ptr2 += len;
+         break;
+       case DW_FORM_ref_udata:
+       case DW_FORM_ref1:
+       case DW_FORM_ref2:
+       case DW_FORM_ref4:
+       case DW_FORM_ref8:
+         switch (form1)
+           {
+           case DW_FORM_ref_udata:
+             value1 = read_uleb128 (ptr1);
+             value2 = read_uleb128 (ptr2);
+             break;
+           case DW_FORM_ref1:
+             value1 = read_8 (ptr1);
+             value2 = read_8 (ptr2);
+             break;
+           case DW_FORM_ref2:
+             value1 = read_16 (ptr1);
+             value2 = read_16 (ptr2);
+             break;
+           case DW_FORM_ref4:
+             value1 = read_32 (ptr1);
+             value2 = read_32 (ptr2);
+             break;
+           case DW_FORM_ref8:
+             value1 = read_64 (ptr1);
+             value2 = read_64 (ptr2);
+             break;
+           default: abort ();
+           }
+         ref1 = off_htab_lookup (die1->die_cu->cu_offset + value1);
+         ref2 = off_htab_lookup (die2->die_cu->cu_offset + value2);
+         assert (ref1 != NULL && ref2 != NULL);
+         if (ref1->u.p1.die_enter >= top_die1->u.p1.die_enter
+             && ref1->u.p1.die_exit <= top_die1->u.p1.die_exit)
+           {
+             /* A reference into a subdie of the DIE being compared.  */
+             if (ref1->u.p1.die_enter - top_die1->u.p1.die_enter
+                 != ref2->u.p1.die_enter - top_die2->u.p1.die_enter
+                 || top_die1->u.p1.die_exit - ref1->u.p1.die_exit
+                    != top_die2->u.p1.die_exit - ref2->u.p1.die_exit)
+               FAIL;
+           }
+         else
+           {
+             dw_die_ref reft1 = ref1, reft2 = ref2;
+             while (reft1->die_toplevel == 0)
+               reft1 = reft1->die_parent;
+             while (reft2->die_toplevel == 0)
+               reft2 = reft2->die_parent;
+             if (ref1->u.p1.die_enter - reft1->u.p1.die_enter
+                 != ref2->u.p1.die_enter - reft2->u.p1.die_enter)
+               FAIL;
+             /* If reft1 (die1 or whatever refers to it is already
+                in the hash table) already has a dup, follow to that
+                dup.  Don't do the same for reft2, {{top_,}die,reft,child}2
+                should always be from the current CU.  */
+             if (die_dup (reft1))
+               reft1 = die_dup (reft1);
+             if (die_eq_1 (reft1, reft2, reft1, reft2) == 0)
+               FAIL;
+           }
+         i++;
+         j++;
+         continue;
+       default:
+         abort ();
+       }
+
+      if ((!IGNORE_LOCUS || old_ptr1)
+         && (ptr1 - old_ptr1 != ptr2 - old_ptr2
+             || memcmp (old_ptr1, old_ptr2, ptr1 - old_ptr1)))
+       FAIL;
+      i++;
+      j++;
+    }
+
+  for (child1 = die1->die_child, child2 = die2->die_child;
+       child1 && child2;
+       child1 = child1->die_sib, child2 = child2->die_sib)
+    if (die_eq_1 (top_die1, top_die2, child1, child2) == 0)
+      FAIL;
+
+  if (child1 || child2)
+    {
+    fail:
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Wrapper around die_eq_1, used as equality function in
+   dup_htab hash table.  If zero (non-match) is returned and
+   any toplevel DIEs have been pushed to the vector in ob obstack,
+   undo the optimistic assignment of die_dup and die_nextdup.  */
+static int
+die_eq (const void *p, const void *q)
+{
+  dw_die_ref die1 = (dw_die_ref) p;
+  dw_die_ref die2 = (dw_die_ref) q;
+  dw_die_ref *arr;
+  unsigned int i, count;
+  int ret;
+
+  if (die1->u.p1.die_hash != die2->u.p1.die_hash
+      || die1->u.p1.die_ref_hash != die2->u.p1.die_ref_hash)
+    return 0;
+  if (die1->die_cu->cu_offset < die2->die_cu->cu_offset)
+    ret = die_eq_1 (die1, die2, die1, die2);
+  else
+    ret = die_eq_1 (die2, die1, die2, die1);
+  count = obstack_object_size (&ob) / sizeof (void *);
+  arr = (dw_die_ref *) obstack_finish (&ob);
+  if (!ret)
+    for (i = 0; i < count; i++)
+      {
+       dw_die_ref die = die_dup (arr[i]);
+       die_nextdup (die) = die_nextdup (arr[i]);
+       die_nextdup (arr[i]) = NULL;
+       die_dup (arr[i]) = NULL;
+       arr[i]->die_remove = 0;
+      }
+  obstack_free (&ob, (void *) arr);
+  return ret;
+}
+
+/* Hash table for finding of matching toplevel DIEs (and all
+   its children together with it).  */
+static htab_t dup_htab;
+
+/* First CU, start of the linked list of CUs, and the tail
+   of that list.  Initially this contains just the original
+   CUs, later on newly created partial units are added
+   to the beginning of the list and optionally .debug_types
+   CUs are added to its tail.  */
+static struct dw_cu *first_cu, *last_cu;
+
+/* Compute approximate size of DIE and all its children together.  */
+static unsigned long
+calc_sizes (dw_die_ref die)
+{
+  unsigned long ret = die->die_size;
+  dw_die_ref child;
+  if (die->die_remove)
+    return 0;
+  for (child = die->die_child; child; child = child->die_sib)
+    ret += calc_sizes (child);
+  return ret;
+}
+
+/* Walk toplevel DIEs in tree rooted by PARENT, and see if they
+   match previously processed DIEs.  */
+static void
+find_dups (dw_die_ref parent)
+{
+  void **slot;
+  dw_die_ref child;
+
+  for (child = parent->die_child; child; child = child->die_sib)
+    {
+      if (child->die_ck_state == CK_KNOWN)
+       {
+         slot = htab_find_slot_with_hash (dup_htab, child,
+                                          child->u.p1.die_hash
+                                          ^ child->u.p1.die_ref_hash,
+                                          INSERT);
+         if (slot == NULL)
+           error (1, ENOMEM, "Not enough memory to find duplicates");
+         if (*slot == NULL)
+           *slot = child;
+       }
+      else if (child->die_named_namespace)
+       find_dups (child);
+    }
+}
+
+#ifdef DEBUG_DUMP_DIES
+/* Debugging helper function to dump hash values to stdout.  */
+static void
+dump_dies (int depth, dw_die_ref die)
+{
+  dw_die_ref child;
+  const char *name = get_AT_string (die, DW_AT_name);
+  printf ("%*s %x %c %x %x %s\n", depth, "", die->die_offset,
+         die->die_ck_state == CK_KNOWN ? 'O' : 'X',
+         (unsigned) die->u.p1.die_hash,
+         (unsigned) die->u.p1.die_ref_hash, name ? name : "");
+  for (child = die->die_child; child; child = child->die_sib)
+    dump_dies (depth + 1, child);
+}
+#endif
+
+/* First phase of the DWARF compression.  Parse .debug_info section,
+   for each CU in it construct internal represetnation for the CU
+   and its DIE tree, compute checksums of DIEs and look for duplicates.  */
+static int
+read_debug_info (DSO *dso)
+{
+  unsigned char *ptr, *endcu, *endsec;
+  uint32_t value;
+  htab_t abbrev;
+  struct abbrev_tag tag, *t;
+
+  dup_htab = htab_try_create (100000, die_hash, die_eq, NULL);
+  if (dup_htab == NULL)
+    {
+      error (0, ENOMEM, "Not enough memory");
+      return 1;
+    }
+
+  obstack_init (&ob);
+  obstack_init (&strob);
+  ptr = debug_sections[DEBUG_INFO].data;
+  endsec = ptr + debug_sections[DEBUG_INFO].size;
+  while (ptr < endsec)
+    {
+      unsigned int cu_offset = ptr - debug_sections[DEBUG_INFO].data;
+      unsigned int tick = 0;
+      int cu_version;
+      struct dw_cu *cu;
+      dw_die_ref *diep, parent, die;
+      bool present;
+      unsigned int debug_line_off;
+
+      if (ptr + 11 > endsec)
+       {
+         error (0, 0, "%s: .debug_info CU header too small", dso->filename);
+         return 1;
+       }
+
+      endcu = ptr + 4;
+      endcu += read_32 (ptr);
+      if (endcu == ptr + 0xffffffff)
+       {
+         error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+         return 1;
+       }
+
+      if (endcu > endsec)
+       {
+         error (0, 0, "%s: .debug_info too small", dso->filename);
+         return 1;
+       }
+
+      cu_version = read_16 (ptr);
+      if (cu_version < 2 || cu_version > 4)
+       {
+         error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
+                cu_version);
+         return 1;
+       }
+
+      value = read_32 (ptr);
+      if (value >= debug_sections[DEBUG_ABBREV].size)
+       {
+         if (debug_sections[DEBUG_ABBREV].data == NULL)
+           error (0, 0, "%s: .debug_abbrev not present", dso->filename);
+         else
+           error (0, 0, "%s: DWARF CU abbrev offset too large",
+                  dso->filename);
+         return 1;
+       }
+
+      if (ptr_size == 0)
+       {
+         ptr_size = read_8 (ptr);
+         if (ptr_size != 4 && ptr_size != 8)
+           {
+             error (0, 0, "%s: Invalid DWARF pointer size %d",
+                    dso->filename, ptr_size);
+             return 1;
+           }
+       }
+      else if (read_8 (ptr) != ptr_size)
+       {
+         error (0, 0, "%s: DWARF pointer size differs between CUs",
+                dso->filename);
+         return 1;
+       }
+
+      abbrev = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value,
+                           false);
+      if (abbrev == NULL)
+       return 1;
+
+      cu = (struct dw_cu *) obstack_alloc (&ob, sizeof (struct dw_cu));
+      memset (cu, '\0', sizeof (*cu));
+      cu->cu_abbrev = abbrev;
+      cu->cu_offset = cu_offset;
+      cu->cu_version = cu_version;
+      diep = &cu->cu_die;
+      parent = NULL;
+      if (first_cu == NULL)
+       first_cu = last_cu = cu;
+      else
+       {
+         last_cu->cu_next = cu;
+         last_cu = cu;
+       }
+
+      while (ptr < endcu)
+       {
+         unsigned int i;
+         unsigned int die_offset = ptr - debug_sections[DEBUG_INFO].data;
+
+         tag.entry = read_uleb128 (ptr);
+         if (tag.entry == 0)
+           {
+             if (parent)
+               {
+                 diep = &parent->die_sib;
+                 parent->u.p1.die_exit = tick++;
+                 parent = parent->die_parent;
+               }
+             else
+               diep = NULL;
+             continue;
+           }
+         if (diep == NULL)
+           {
+             error (0, 0, "%s: Wrong .debug_info DIE tree", dso->filename);
+             return 1;
+           }
+         t = htab_find_with_hash (abbrev, &tag, tag.entry);
+         if (t == NULL)
+           {
+             error (0, 0, "%s: Could not find DWARF abbreviation %d",
+                    dso->filename, tag.entry);
+             return 1;
+           }
+         if (parent == NULL
+             || parent->die_parent == NULL
+             || parent->die_named_namespace)
+           {
+             die = (dw_die_ref)
+                   obstack_alloc (&ob, sizeof (struct dw_toplevel_die));
+             memset (die, '\0', sizeof (struct dw_toplevel_die));
+             die->die_toplevel = 1;
+           }
+         else
+           {
+             die = (dw_die_ref) obstack_alloc (&ob, sizeof (struct dw_die));
+             memset (die, '\0', sizeof (*die));
+           }
+         *diep = die;
+         die->die_tag = t->tag;
+         die->die_abbrev = t;
+         die->die_offset = die_offset;
+         die->die_cu = cu;
+         die->die_parent = parent;
+         die->u.p1.die_enter = tick;
+         die->u.p1.die_exit = tick++;
+         if (t->children)
+           {
+             diep = &die->die_child;
+             parent = die;
+           }
+         else
+           diep = &die->die_sib;
+         for (i = 0; i < t->nattr; ++i)
+           {
+             uint32_t form = t->attr[i].form;
+             size_t len = 0;
+
+             while (form == DW_FORM_indirect)
+               {
+                 form = read_uleb128 (ptr);
+                 if (ptr >= endcu)
+                   {
+                     error (0, 0, "%s: Attributes extend beyond end of CU",
+                            dso->filename);
+                     return 1;
+                   }
+               }
+             switch (form)
+               {
+               case DW_FORM_ref_addr:
+                 ptr += cu->cu_version == 2 ? ptr_size : 4;
+                 break;
+               case DW_FORM_addr:
+                 ptr += ptr_size;
+                 break;
+               case DW_FORM_flag_present:
+                 break;
+               case DW_FORM_ref1:
+               case DW_FORM_flag:
+               case DW_FORM_data1:
+                 ++ptr;
+                 break;
+               case DW_FORM_ref2:
+               case DW_FORM_data2:
+                 ptr += 2;
+                 break;
+               case DW_FORM_ref4:
+               case DW_FORM_data4:
+               case DW_FORM_sec_offset:
+                 ptr += 4;
+                 break;
+               case DW_FORM_ref8:
+               case DW_FORM_data8:
+               case DW_FORM_ref_sig8:
+                 ptr += 8;
+                 break;
+               case DW_FORM_sdata:
+               case DW_FORM_ref_udata:
+               case DW_FORM_udata:
+                 read_uleb128 (ptr);
+                 break;
+               case DW_FORM_strp:
+                 ptr += 4;
+                 if (t->attr[i].attr == DW_AT_name
+                     && (die->die_tag == DW_TAG_namespace
+                         || die->die_tag == DW_TAG_module)
+                     && die->die_parent != NULL
+                     && (die->die_parent->die_parent == NULL
+                         || die->die_parent->die_named_namespace))
+                   die->die_named_namespace = 1;
+                 break;
+               case DW_FORM_string:
+                 ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+                 if (t->attr[i].attr == DW_AT_name
+                     && (die->die_tag == DW_TAG_namespace
+                         || die->die_tag == DW_TAG_module)
+                     && die->die_parent != NULL
+                     && (die->die_parent->die_parent == NULL
+                         || die->die_parent->die_named_namespace))
+                   die->die_named_namespace = 1;
+                 break;
+               case DW_FORM_indirect:
+                 abort ();
+               case DW_FORM_block1:
+                 len = *ptr++;
+                 break;
+               case DW_FORM_block2:
+                 len = read_16 (ptr);
+                 form = DW_FORM_block1;
+                 break;
+               case DW_FORM_block4:
+                 len = read_32 (ptr);
+                 form = DW_FORM_block1;
+                 break;
+               case DW_FORM_block:
+               case DW_FORM_exprloc:
+                 len = read_uleb128 (ptr);
+                 form = DW_FORM_block1;
+                 break;
+               default:
+                 error (0, 0, "%s: Unknown DWARF DW_FORM_%d",
+                        dso->filename, form);
+                 return 1;
+               }
+
+             if (ptr >= endcu)
+               {
+                 error (0, 0, "%s: Attributes extend beyond end of CU",
+                        dso->filename);
+                 return 1;
+               }
+
+             if (form == DW_FORM_block1)
+               {
+                 if (len >= (size_t) (endcu - ptr))
+                   {
+                     error (0, 0, "%s: Attributes extend beyond end of CU",
+                            dso->filename);
+                     return 1;
+                   }
+
+                 if (t->attr[i].attr > DW_AT_linkage_name
+                     && (t->attr[i].attr < DW_AT_MIPS_fde
+                         || t->attr[i].attr > DW_AT_MIPS_has_inlines)
+                     && (t->attr[i].attr < DW_AT_sf_names
+                         || t->attr[i].attr > DW_AT_body_end)
+                     && (t->attr[i].attr < DW_AT_GNU_call_site_value
+                         || t->attr[i].attr > DW_AT_GNU_call_site_target_clobbered))
+                   {
+                     error (0, 0, "%s: Unknown DWARF DW_AT_%d with block DW_FORM",
+                            dso->filename, t->attr[i].attr);
+                     return 1;
+                   }
+
+                 ptr += len;
+               }
+           }
+         die->die_size = (ptr - debug_sections[DEBUG_INFO].data)
+                         - die_offset;
+         if (off_htab_add_die (die))
+           return 1;
+       }
+
+      if (cu->cu_die == NULL
+         || (cu->cu_die->die_tag != DW_TAG_compile_unit
+             && cu->cu_die->die_tag != DW_TAG_partial_unit)
+         || cu->cu_die->die_sib != NULL)
+       {
+         error (0, 0, "%s: .debug_info section chunk doesn't contain a single"
+                       " compile_unit or partial_unit", dso->filename);
+         return 1;
+       }
+
+      cu->cu_comp_dir = get_AT_string (cu->cu_die, DW_AT_comp_dir);
+      debug_line_off = get_AT_int (cu->cu_die, DW_AT_stmt_list, &present);
+      if (present && read_debug_line (dso, cu, debug_line_off))
+       return 1;
+
+      if (checksum_die (dso, NULL, cu->cu_die))
+       return 1;
+      checksum_ref_die (NULL, cu->cu_die, NULL, NULL);
+
+#ifdef DEBUG_DUMP_DIES
+      dump_dies (0, cu->cu_die);
+#endif
+
+      find_dups (cu->cu_die);
+    }
+
+  htab_delete (dup_htab);
+  dup_htab = NULL;
+  return 0;
+}
+
+/* Compare function called from qsort, which should ensure that
+   dup candidate dies with the same set of referrer CUs are
+   adjacent.  */
+static int
+partition_cmp (const void *p, const void *q)
+{
+  dw_die_ref die1 = *(dw_die_ref *) p;
+  dw_die_ref die2 = *(dw_die_ref *) q;
+  dw_die_ref ref1, ref2;
+  struct dw_cu *last_cu1 = NULL, *last_cu2 = NULL;
+  for (ref1 = die_nextdup (die1), ref2 = die_nextdup (die2);;
+       ref1 = die_nextdup (ref1), ref2 = die_nextdup (ref2))
+    {
+      while (ref1 && ref1->die_cu == last_cu1)
+       ref1 = die_nextdup (ref1);
+      if (ref1 == NULL)
+       break;
+      while (ref2 && ref2->die_cu == last_cu2)
+       ref2 = die_nextdup (ref2);
+      if (ref2 == NULL)
+       break;
+      last_cu1 = ref1->die_cu;
+      last_cu2 = ref2->die_cu;
+      if (last_cu1->cu_offset < last_cu2->cu_offset)
+       return -1;
+      else if (last_cu1->cu_offset > last_cu2->cu_offset)
+       return 1;
+    }
+  if (ref1)
+    return -1;
+  if (ref2)
+    return 1;
+  /* The rest is just to keep sort stable.  */
+  if (die1->die_offset < die2->die_offset)
+    return -1;
+  if (die1->die_offset > die2->die_offset)
+    return 1;
+  return 0;
+}
+
+/* Search for duplicate removal reference DIE candidates
+   in tree rooted by PARENT.  */
+static void
+partition_find_dups (struct obstack *vec, dw_die_ref parent)
+{
+  dw_die_ref child;
+  for (child = parent->die_child; child; child = child->die_sib)
+    {
+      if (die_nextdup (child) != NULL
+         && die_dup (child) == NULL
+         && child->die_offset != -1U)
+       obstack_ptr_grow (vec, child);
+      else if (child->die_named_namespace)
+       partition_find_dups (vec, child);
+    }
+}
+
+/* Copy DIE tree of DIE, as children of new DIE PARENT.  */
+static dw_die_ref
+copy_die_tree (dw_die_ref parent, dw_die_ref die)
+{
+  dw_die_ref child, new_child, *diep;
+  dw_die_ref new_die;
+  if (die->die_toplevel)
+    {
+      new_die = (dw_die_ref)
+               obstack_alloc (&ob, sizeof (struct dw_toplevel_die));
+      memset (new_die, '\0', sizeof (*new_die));
+      new_die->die_toplevel = 1;
+      die_dup (die) = new_die;
+      die_nextdup (new_die) = die;
+      if (!die->die_op_type_referenced)
+       die->die_remove = 1;
+    }
+  else
+    {
+      new_die = (dw_die_ref)
+               obstack_alloc (&ob, sizeof (struct dw_die));
+      memset (new_die, '\0', sizeof (*new_die));
+    }
+  new_die->die_parent = parent;
+  new_die->die_tag = die->die_tag;
+  new_die->die_offset = -1U;
+  new_die->die_cu = parent->die_cu;
+  new_die->die_size = die->die_size;
+  diep = &new_die->die_child;
+  for (child = die->die_child; child; child = child->die_sib)
+    {
+      new_child = copy_die_tree (new_die, child);
+      *diep = new_child;
+      diep = &new_child->die_sib;
+    }
+  return new_die;
+}
+
+/* Decide what DIEs matching in multiple CUs might be worthwhile
+   to be moved into partial units, construct those partial units.  */
+static int
+partition_dups (void)
+{
+  struct obstack vec;
+  unsigned int cu_off = 0;
+  struct dw_cu *cu, *first_partial_cu = NULL, *last_partial_cu = NULL;
+  size_t vec_size, i, j;
+  obstack_init (&vec);
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    {
+      partition_find_dups (&vec, cu->cu_die);
+      vec_size = obstack_object_size (&vec) / sizeof (void *);
+      if (vec_size != 0)
+       {
+         dw_die_ref *arr = (dw_die_ref *) obstack_finish (&vec);
+         qsort (arr, vec_size, sizeof (dw_die_ref), partition_cmp);
+         for (i = 0; i < vec_size; i = j)
+           {
+             dw_die_ref ref;
+             size_t cnt = 0, size = 0, k, orig_size, new_size, namespaces = 0;
+             for (j = i + 1; j < vec_size; j++)
+               {
+                 dw_die_ref ref1, ref2;
+                 struct dw_cu *last_cu1 = NULL, *last_cu2 = NULL;
+                 size_t this_cnt = 0;
+                 for (ref1 = die_nextdup (arr[i]), ref2 = die_nextdup (arr[j]);;
+                      ref1 = die_nextdup (ref1), ref2 = die_nextdup (ref2))
+                   {
+                     while (ref1 && ref1->die_cu == last_cu1)
+                       ref1 = die_nextdup (ref1);
+                     if (ref1 == NULL)
+                       break;
+                     while (ref2 && ref2->die_cu == last_cu2)
+                       ref2 = die_nextdup (ref2);
+                     if (ref2 == NULL)
+                       break;
+                     last_cu1 = ref1->die_cu;
+                     last_cu2 = ref2->die_cu;
+                     if (last_cu1 != last_cu2)
+                       break;
+                     else
+                       this_cnt++;
+                   }
+                 if (ref1 || ref2)
+                   break;
+                 cnt = this_cnt;
+               }
+             if (cnt == 0)
+               for (ref = die_nextdup (arr[i]); ref; ref = die_nextdup (ref))
+                 cnt++;
+             for (k = i; k < j; k++)
+               {
+                 size += calc_sizes (arr[k]);
+                 for (ref = arr[k]->die_parent;
+                      ref->die_named_namespace && die_dup (ref) == NULL;
+                      ref = ref->die_parent)
+                   {
+                     die_dup (ref) = arr[k];
+                     namespaces++;
+                   }
+               }
+             if (namespaces)
+               {
+                 for (k = i; k < j; k++)
+                   for (ref = arr[k]->die_parent; ref->die_named_namespace;
+                        ref = ref->die_parent)
+                     die_dup (ref) = NULL;
+               }
+             orig_size = size * (cnt + 1);
+             /* Estimated size of CU header and DW_TAG_partial_unit
+                with DW_AT_stmt_list and DW_AT_comp_dir attributes
+                21 (also child end byte), plus in each CU referencing it
+                DW_TAG_imported_unit with DW_AT_import attribute
+                (5 or 9 bytes (the latter for DWARF2 and ptr_size 8)).
+                For DW_TAG_namespace or DW_TAG_module needed as
+                parents of the DIEs conservatively assume 10 bytes
+                for the abbrev index, DW_AT_name attribute and
+                DW_AT_sibling attribute and child end.  */
+             new_size = size + 21
+                        + (cu->cu_version == 2 ? 1 + ptr_size : 5) * (cnt + 1)
+                        + 10 * namespaces;
+             if (orig_size > new_size)
+               {
+                 dw_die_ref die, *diep;
+                 struct dw_cu *partial_cu
+                   = (struct dw_cu *)
+                     obstack_alloc (&ob, sizeof (struct dw_cu));
+                 memset (partial_cu, '\0', sizeof (*partial_cu));
+                 partial_cu->cu_offset = cu_off++;
+                 partial_cu->cu_version = cu->cu_version;
+                 if (first_partial_cu == NULL)
+                   first_partial_cu = last_partial_cu = partial_cu;
+                 else
+                   {
+                     last_partial_cu->cu_next = partial_cu;
+                     last_partial_cu = partial_cu;
+                   }
+                 die = (dw_die_ref)
+                       obstack_alloc (&ob, sizeof (struct dw_toplevel_die));
+                 memset (die, '\0', sizeof (struct dw_toplevel_die));
+                 die->die_toplevel = 1;
+                 partial_cu->cu_die = die;
+                 die->die_tag = DW_TAG_partial_unit;
+                 die->die_offset = -1U;
+                 die->die_cu = partial_cu;
+                 die_nextdup (die) = cu->cu_die;
+                 die->die_size = 9;
+                 diep = &die->die_child;
+                 for (k = i; k < j; k++)
+                   {
+                     dw_die_ref child = copy_die_tree (die, arr[k]);
+                     for (ref = die_nextdup (arr[k]);
+                          ref; ref = die_nextdup (ref))
+                       die_dup (ref) = child;
+                     if (namespaces)
+                       {
+                         for (ref = arr[k]->die_parent;
+                              ref->die_named_namespace
+                              && die_dup (ref) == NULL;
+                              ref = ref->die_parent)
+                           {
+                             dw_die_ref namespc
+                               = (dw_die_ref)
+                                 obstack_alloc (&ob,
+                                                sizeof (struct
+                                                        dw_toplevel_die));
+                             memset (namespc, '\0',
+                                     sizeof (struct dw_toplevel_die));
+                             namespc->die_toplevel = 1;
+                             namespc->die_tag = ref->die_tag;
+                             namespc->die_offset = -1U;
+                             namespc->die_cu = partial_cu;
+                             die_nextdup (namespc) = ref;
+                             namespc->die_child = child;
+                             namespc->die_parent = die;
+                             namespc->die_size = 9;
+                             namespc->die_named_namespace = 1;
+                             child->die_parent = namespc;
+                             die_dup (ref) = namespc;
+                             child = namespc;
+                           }
+                         if (die_dup (ref) != NULL)
+                           {
+                             dw_die_ref *diep2;
+                             for (diep2 = &die_dup (ref)->die_child->die_sib;
+                                  *diep2; diep2 = &(*diep2)->die_sib)
+                               ;
+                             *diep2 = child;
+                             child->die_parent = die_dup (ref);
+                             continue;
+                           }
+                       }
+                     *diep = child;
+                     diep = &child->die_sib;
+                   }
+                 if (namespaces)
+                   {
+                     for (k = i; k < j; k++)
+                       for (ref = arr[k]->die_parent;
+                            ref->die_named_namespace;
+                            ref = ref->die_parent)
+                         die_dup (ref) = NULL;
+                   }
+               }
+             else
+               {
+                 dw_die_ref next;
+                 for (k = i; k < j; k++)
+                   {
+                     for (ref = arr[k]; ref; ref = next)
+                       {
+                         next = die_nextdup (ref);
+                         die_dup (ref) = NULL;
+                         die_nextdup (ref) = NULL;
+                         ref->die_remove = 0;
+                       }
+                     arr[k] = NULL;
+                   }
+               }
+           }
+         obstack_free (&vec, (void *) arr);
+       }
+    }
+  if (first_partial_cu)
+    {
+      last_partial_cu->cu_next = first_cu;
+      first_cu = first_partial_cu;
+    }
+  obstack_free (&vec, NULL);
+  return 0;
+}
+
+/* The create_import_tree function below and all its helper
+   data structures and functions attempt to optimize the size of
+   DW_TAG_imported_unit DIEs, from the initial assumption that
+   each CU that needs to include some newly created DW_TAG_partial_unit
+   will contain DW_TAG_imported_unit for each such partial unit (PU)
+   (so basically a bipartite graph with CUs and PUs as nodes
+   and DW_TAG_imported_unit DIEs as edges) into a tree, where some
+   of the partial units may also include DW_TAG_imported_unit
+   DIEs, or when beneficial new PUs are created to hold some
+   DW_TAG_imported_unit DIEs.  */
+
+struct import_edge;
+
+/* Structure describing details about a CU or PU (i.e. a node
+   in the graph).  */
+struct import_cu
+{
+  /* Corresponding CU.  CU->u1.cu_icu points back to this
+     structure while in create_import_tree.  */
+  struct dw_cu *cu;
+  /* Linked list of incoming resp. outgoing edges.  */
+  struct import_edge *incoming, *outgoing;
+  /* Next import_cu (used to chain PUs together).  */
+  struct import_cu *next;
+  /* Number of incoming resp. outgoing edges.  */
+  unsigned int incoming_count, outgoing_count;
+  /* Index.  Lowest indexes are assigned to partition_dups
+     created PUs (sorted by decreasing number of incoming
+     edges at the start), then referencing CUs
+     (similarly, sorted by decreasing number of outgoing
+     edges at the start), then optionally any PUs
+     created by create_import_tree.  */
+  unsigned int idx;
+  /* Flag used during PU merging, set for PUs already considered
+     for merging for the given first PU.  */
+  bool seen;
+};
+
+/* An edge in a linked list.  */
+struct import_edge
+{
+  struct import_cu *icu;
+  struct import_edge *next;
+};
+
+/* Called through qsort to sort an array of edges by decreasing
+   incoming resp. outgoing_count (this is called when the graph
+   is bipartite, so CUs only have non-zero outgoing_count
+   and PUs only have non-zero incoming_count).  */
+static int
+import_edge_cmp (const void *p, const void *q)
+{
+  struct import_edge *e1 = (struct import_edge *) p;
+  struct import_edge *e2 = (struct import_edge *) q;
+  if (e1->icu->incoming_count > e2->icu->incoming_count)
+    return -1;
+  if (e1->icu->incoming_count < e2->icu->incoming_count)
+    return 1;
+  if (e1->icu->outgoing_count > e2->icu->outgoing_count)
+    return -1;
+  if (e1->icu->outgoing_count < e2->icu->outgoing_count)
+    return 1;
+  /* The rest is just to keep qsort stable.  */
+  if (e1->icu->cu->cu_offset < e2->icu->cu->cu_offset)
+    return -1;
+  if (e1->icu->cu->cu_offset > e2->icu->cu->cu_offset)
+    return 1;
+  return 0;
+}
+
+/* Called through qsort to sort an array of CUs/PUs by decreasing
+   incoming resp. outgoing_count (this is called when the graph
+   is bipartite, so CUs only have non-zero outgoing_count
+   and PUs only have non-zero incoming_count).  */
+static int
+import_cu_cmp (const void *p, const void *q)
+{
+  struct import_cu *c1 = *(struct import_cu **) p;
+  struct import_cu *c2 = *(struct import_cu **) q;
+  if (c1->incoming_count > c2->incoming_count)
+    return -1;
+  if (c1->incoming_count < c2->incoming_count)
+    return 1;
+  if (c1->outgoing_count > c2->outgoing_count)
+    return -1;
+  if (c1->outgoing_count < c2->outgoing_count)
+    return 1;
+  /* The rest is just to keep qsort stable.  */
+  if (c1->cu->cu_offset < c2->cu->cu_offset)
+    return -1;
+  if (c1->cu->cu_offset > c2->cu->cu_offset)
+    return 1;
+  return 0;
+}
+
+/* Freelist for removed edges.  */
+static struct import_edge *edge_freelist;
+
+/* Remove edges in linked list EP that refer to CUS, which
+   is an array of CUCOUNT CUs/PUs.  If ADD is true, additionally
+   add a new edge at the end of the linked list and return it.  */
+static struct import_edge *
+remove_import_edges (struct import_edge **ep, struct import_cu **cus,
+                    unsigned int cucount, bool add)
+{
+  unsigned int i = 0;
+  struct import_edge *e, *efirst = NULL;
+  while (*ep)
+    if (i < cucount && (*ep)->icu == cus[i])
+      {
+       e = *ep;
+       *ep = e->next;
+       if (efirst == NULL)
+         efirst = e;
+       else
+         {
+           e->next = edge_freelist;
+           edge_freelist = e;
+         }
+       i++;
+       if (i == cucount && !add)
+         return NULL;
+      }
+    else
+      ep = &(*ep)->next;
+  assert (i == cucount);
+  *ep = efirst;
+  efirst->next = NULL;
+  return efirst;
+}
+
+#ifdef DEBUG_VERIFY_EDGES
+/* Helper function for debugging create_import_tree.  Verify
+   various invariants for CU/PU IPU.  */
+static void
+verify_edges_1 (struct import_cu *ipu, unsigned int *ic, unsigned int *oc)
+{
+  struct import_edge *e1, *e2;
+  unsigned int last_idx = 0, count;
+  for (e1 = ipu->incoming, count = 0; e1; e1 = e1->next)
+    {
+      assert (count == 0 || e1->icu->idx > last_idx);
+      last_idx = e1->icu->idx;
+      count++;
+      for (e2 = e1->icu->outgoing; e2; e2 = e2->next)
+       if (e2->icu == ipu)
+         break;
+      assert (e2);
+    }
+  assert (ipu->incoming_count == count);
+  for (e1 = ipu->outgoing, count = 0; e1; e1 = e1->next)
+    {
+      assert (count == 0 || e1->icu->idx > last_idx);
+      last_idx = e1->icu->idx;
+      count++;
+      for (e2 = e1->icu->incoming; e2; e2 = e2->next)
+       if (e2->icu == ipu)
+         break;
+      assert (e2);
+    }
+  assert (ipu->outgoing_count == count);
+  *ic += ipu->incoming_count;
+  *oc += ipu->outgoing_count;
+}
+
+/* Helper function for debugging create_import_tree.  Call verify_edges_1
+   on all CUs and PUs.  */
+void
+verify_edges (struct import_cu **ipus, unsigned int npus, unsigned int ncus)
+{
+  struct import_cu *ipu;
+  unsigned int i, ic = 0, oc = 0;
+  for (ipu = ipus[0]; ipu; ipu = ipu->next)
+    verify_edges_1 (ipu, &ic, &oc);
+  for (i = 0; i < ncus; i++)
+    verify_edges_1 (ipus[i + npus], &ic, &oc);
+  assert (ic == oc);
+}
+#endif
+
+/* Function to optimize the size of DW_TAG_imported_unit DIEs by
+   creating an inclusion tree, instead of each CU importing all
+   PUs it needs directly, by optionally creating new PUs or
+   adding DW_TAG_imported_unit to the already created PUs.
+   At the end this function constructs any new PUs needed, and
+   adds DW_TAG_imported_unit DIEs to them as well as the CUs
+   and partition_dups created PUs.  */
+static int
+create_import_tree (void)
+{
+  struct dw_cu *pu, *cu, *last_partial_cu = NULL;
+  struct obstack iob;
+  unsigned int i, new_pu_version = 2, min_cu_version, npus, ncus;
+  struct import_cu **ipus, *ipu, *icu;
+  unsigned int cu_off;
+  unsigned int puidx;
+  struct import_cu *last_pu, *pu_freelist = NULL;
+  /* size doesn't count anything already created before this
+     function (partial units etc.) or already preexisting, just
+     initially the cumulative sizes of DW_TAG_imported_unit DIEs
+     that would need to be added, and if some new DW_TAG_partial_unit
+     CUs are going to be created as a result of this routine, that size
+     too.  DW_TAG_imported_unit has size 5 (for DWARF3+) or 1 + ptr_size
+     (DWARF2), DW_TAG_partial_unit has size 13 (11 CU header + 1 byte
+     abbrev number + 1 byte child end).  */
+  unsigned int size = 0;
+  /* Size of DW_TAG_imported_unit if the same everywhere, otherwise
+     (mixing DWARF2 and DWARF3+ with ptr_size != 4) 0.  */
+  unsigned int edge_cost = 0;
+  /* Number of bytes needed for outgoing edges of PUs created by
+     this function (which all have DWARF version new_pu_version).  */
+  unsigned int new_edge_cost;
+
+  /* If no PUs were created, there is nothing to do here.  */
+  if (first_cu == NULL || first_cu->cu_abbrev != NULL)
+    return 0;
+
+  obstack_init (&iob);
+  min_cu_version = first_cu->cu_version;
+  /* First construct a bipartite graph between CUs and PUs.  */
+  for (pu = first_cu, npus = 0;
+       pu && pu->cu_abbrev == NULL; pu = pu->cu_next, npus++)
+    {
+      dw_die_ref die, rdie;
+      struct dw_cu *prev_cu;
+
+      last_partial_cu = pu;
+      if (pu->cu_version > new_pu_version)
+       new_pu_version = pu->cu_version;
+      if (pu->cu_version < min_cu_version)
+       min_cu_version = pu->cu_version;
+      ipu = (struct import_cu *) obstack_alloc (&iob, sizeof (*ipu));
+      memset (ipu, 0, sizeof (*ipu));
+      ipu->cu = pu;
+      pu->u1.cu_icu = ipu;
+      for (rdie = pu->cu_die->die_child;
+          rdie->die_named_namespace; rdie = rdie->die_child)
+       ;
+      assert (rdie->die_toplevel);
+      for (die = die_nextdup (rdie), prev_cu = NULL;
+          die; die = die_nextdup (die))
+       {
+         if (die->die_cu == prev_cu)
+           continue;
+         ipu->incoming_count++;
+         size += 1 + (die->die_cu->cu_version == 2 ? ptr_size : 4);
+         prev_cu = die->die_cu;
+       }
+      ipu->incoming = (struct import_edge *)
+                      obstack_alloc (&iob, ipu->incoming_count
+                                           * sizeof (*ipu->incoming));
+      for (die = die_nextdup (rdie), i = 0, prev_cu = NULL;
+          die; die = die_nextdup (die))
+       {
+         if (die->die_cu == prev_cu)
+           continue;
+         icu = die->die_cu->u1.cu_icu;
+         if (icu == NULL)
+           {
+             icu = (struct import_cu *) obstack_alloc (&iob, sizeof (*ipu));
+             memset (icu, 0, sizeof (*icu));
+             icu->cu = die->die_cu;
+             die->die_cu->u1.cu_icu = icu;
+           }
+         ipu->incoming[i++].icu = icu;
+         icu->outgoing_count++;
+         prev_cu = die->die_cu;
+       }
+    }
+  for (cu = pu, ncus = 0; cu; cu = cu->cu_next)
+    if (cu->u1.cu_icu)
+      {
+       ncus++;
+       if (cu->cu_version > new_pu_version)
+         new_pu_version = cu->cu_version;
+       if (cu->cu_version < min_cu_version)
+         min_cu_version = cu->cu_version;
+       cu->u1.cu_icu->outgoing
+         = (struct import_edge *)
+           obstack_alloc (&iob, cu->u1.cu_icu->outgoing_count
+                                * sizeof (*cu->u1.cu_icu->outgoing));
+       cu->u1.cu_icu->outgoing_count = 0;
+      }
+  if (ptr_size == 4 || min_cu_version > 2)
+    edge_cost = 5;
+  else if (new_pu_version == 2)
+    edge_cost = 1 + ptr_size;
+  new_edge_cost = new_pu_version == 2 ? 1 + ptr_size : 5;
+  for (pu = first_cu; pu && pu->cu_abbrev == NULL; pu = pu->cu_next)
+    {
+      ipu = pu->u1.cu_icu;
+      for (i = 0; i < ipu->incoming_count; i++)
+       {
+         icu = ipu->incoming[i].icu;
+         icu->outgoing[icu->outgoing_count++].icu = ipu;
+       }
+    }
+  ipus = (struct import_cu **)
+        obstack_alloc (&iob, (npus + ncus) * sizeof (*ipus));
+  for (pu = first_cu, npus = 0;
+       pu && pu->cu_abbrev == NULL; pu = pu->cu_next, npus++)
+    {
+      ipu = pu->u1.cu_icu;
+      qsort (ipu->incoming, ipu->incoming_count, sizeof (*ipu->incoming),
+            import_edge_cmp);
+      for (i = 0; i < ipu->incoming_count; i++)
+       {
+         ipu->incoming[i].next
+           = i != ipu->incoming_count - 1 ? &ipu->incoming[i + 1] : NULL;
+       }
+      ipus[npus] = ipu;
+    }
+  for (cu = pu, ncus = 0; cu; cu = cu->cu_next)
+    if (cu->u1.cu_icu)
+      {
+       icu = cu->u1.cu_icu;
+       qsort (icu->outgoing, icu->outgoing_count, sizeof (*icu->outgoing),
+              import_edge_cmp);
+       for (i = 0; i < icu->outgoing_count - 1; i++)
+         icu->outgoing[i].next = &icu->outgoing[i + 1];
+       icu->outgoing[i].next = NULL;
+       ipus[npus + ncus] = icu;
+       ncus++;
+      }
+  qsort (ipus, npus, sizeof (*ipus), import_cu_cmp);
+  qsort (ipus + npus, ncus, sizeof (*ipus), import_cu_cmp);
+  for (puidx = 0; puidx < npus; puidx++)
+    {
+      ipus[puidx]->idx = puidx;
+      if (puidx + 1 < npus)
+       ipus[puidx]->next = ipus[puidx + 1];
+    }
+  for (; puidx < npus + ncus; puidx++)
+    ipus[puidx]->idx = puidx;
+  last_pu = ipus[npus - 1];
+  /* Now, for the above constructed bipartite graph, find K x,2 components
+     where x >= 5 (for DWARF3 and above or ptr_size 4, for DWARF2 and
+     ptr_size 8 it can be even x == 4) and add a new PU node, where all
+     CUs from the component will point to the new PU node and that new PU
+     will point to all the destination PUs.  In theory with DWARF2
+     and ptr_size 1 we could need x >= 9.  */
+  for (i = 0; i < npus - 1; i++)
+    {
+      struct import_cu *pudst[2], *pusrc[10];
+      struct import_edge *e1, *e2, *e3, *e4;
+      struct import_edge *e1next, *e2next, *e3next;
+      pudst[0] = ipus[i];
+      for (e1 = pudst[0]->incoming; e1; e1 = e1next)
+       {
+         e1next = e1->next;
+         if (e1->icu->cu == NULL)
+           break;
+         for (e2 = e1->icu->outgoing; e2; e2 = e2next)
+           {
+             unsigned int srccount, dstcount, cost;
+             struct import_cu *npu = NULL;
+             struct import_edge **ep = NULL;
+
+             e2next = e2->next;
+             if (e2->icu->idx <= pudst[0]->idx)
+               continue;
+             if (e2->icu->cu == NULL)
+               break;
+
+             pudst[1] = e2->icu;
+             pusrc[0] = e1->icu;
+             srccount = 1;
+             cost = edge_cost;
+             if (!edge_cost)
+               cost = pusrc[0]->cu->cu_version == 2 ? 1 + ptr_size : 5;
+             for (e3 = e1next; e3; e3 = e3next)
+               {
+                 e3next = e3->next;
+                 if (e3->icu->cu == NULL)
+                   break;
+                 dstcount = 0;
+                 for (e4 = e3->icu->outgoing; e4; e4 = e4->next)
+                   {
+                     if (e4->icu == pudst[0])
+                       dstcount++;
+                     else if (e4->icu == pudst[1])
+                       {
+                         dstcount++;
+                         break;
+                       }
+                     else if (e4->icu->idx > pudst[1]->idx)
+                       break;
+                   }
+                 if (dstcount != 2)
+                   continue;
+                 if (npu == NULL)
+                   {
+                     pusrc[srccount] = e3->icu;
+                     cost += edge_cost;
+                     if (!edge_cost)
+                       cost += pusrc[srccount]->cu->cu_version == 2
+                               ? 1 + ptr_size : 5;
+                     srccount++;
+                     if ((dstcount - 1) * cost
+                         > 13 + dstcount * new_edge_cost)
+                       {
+                         unsigned int j;
+
+                         e2next = NULL;
+                         if (pu_freelist)
+                           {
+                             npu = pu_freelist;
+                             pu_freelist = pu_freelist->next;
+                           }
+                         else
+                           npu = (struct import_cu *)
+                                 obstack_alloc (&iob, sizeof (*npu));
+                         memset (npu, 0, sizeof (*npu));
+                         npu->incoming_count = srccount;
+                         npu->outgoing_count = dstcount;
+                         npu->idx = puidx++;
+                         last_pu->next = npu;
+                         last_pu = npu;
+                         for (j = 0; j < srccount; j++)
+                           {
+                             if (e1next && e1next->icu == pusrc[j])
+                               e1next = e1next->next;
+                             remove_import_edges (&pusrc[j]->outgoing, pudst,
+                                                  dstcount, true)->icu = npu;
+                             pusrc[j]->outgoing_count -= dstcount - 1;
+                           }
+                         for (j = 0; j < dstcount; j++)
+                           {
+                             remove_import_edges (&pudst[j]->incoming, pusrc,
+                                                  srccount, true)->icu = npu;
+                             pudst[j]->incoming_count -= srccount - 1;
+                           }
+                         npu->incoming = edge_freelist;
+                         for (j = 0, e4 = npu->incoming; j < srccount; j++)
+                           {
+                             e4->icu = pusrc[j];
+                             if (j == srccount - 1)
+                               {
+                                 edge_freelist = e4->next;
+                                 e4->next = NULL;
+                                 ep = &e4->next;
+                               }
+                             else
+                               e4 = e4->next;
+                           }
+                         npu->outgoing = edge_freelist;
+                         for (j = 0, e4 = npu->outgoing; j < dstcount; j++)
+                           {
+                             e4->icu = pudst[j];
+                             if (j == dstcount - 1)
+                               {
+                                 edge_freelist = e4->next;
+                                 e4->next = NULL;
+                               }
+                             else
+                               e4 = e4->next;
+                           }
+                         size -= (dstcount - 1) * cost;
+                         size += 13 + dstcount * new_edge_cost;
+                       }
+                   }
+                 else
+                   {
+                     unsigned int j;
+
+                     pusrc[srccount] = e3->icu;
+                     cost = edge_cost;
+                     if (!edge_cost)
+                       cost = pusrc[srccount]->cu->cu_version == 2
+                              ? 1 + ptr_size : 5;
+                     if (e1next && e1next->icu == pusrc[srccount])
+                       e1next = e1next->next;
+                     remove_import_edges (&pusrc[srccount]->outgoing, pudst,
+                                          dstcount, true)->icu = npu;
+                     pusrc[srccount]->outgoing_count -= dstcount - 1;
+                     for (j = 0; j < dstcount; j++)
+                       {
+                         remove_import_edges (&pudst[j]->incoming,
+                                              pusrc + srccount, 1, false);
+                         pudst[j]->incoming_count--;
+                       }
+                     *ep = edge_freelist;
+                     edge_freelist = edge_freelist->next;
+                     npu->incoming_count++;
+                     (*ep)->icu = pusrc[srccount];
+                     (*ep)->next = NULL;
+                     ep = &(*ep)->next;
+                     size -= (dstcount - 1) * cost;
+                   }
+               }
+           }
+       }
+    }
+  /* Try to merge PUs which have the same set of referrers if
+     beneficial, or if one PU has a subset of referrers of the
+     other, attempt to replace all the incoming edges from the
+     referrers intersection to the PU with larger number of
+     incoming edges by an edge from the other PU.  */
+  for (ipu = ipus[0]; ipu; ipu = ipu->next)
+    {
+      struct import_edge *e1, *e2, *e3, *e4, **e1p, **ep;
+      for (e1p = &ipu->incoming, e1 = *e1p;
+          e1; e1 = *e1p != e1 ? *e1p : (e1p = &e1->next, e1->next))
+       {
+         for (e2 = e1->icu->outgoing; e2; e2 = e2->next)
+           {
+             unsigned int size_inc, size_dec;
+             struct import_cu *ipu2 = e2->icu, *ipusub, *ipusup;
+             /* True if IPU's src set might be a subset
+                of IPU2's src set.  */
+             bool maybe_subset;
+             /* True if IPU's src set might be a superset
+                of IPU2's src set.  */
+             bool maybe_superset;
+             unsigned int intersection;
+
+             if (ipu2->idx <= ipu->idx || ipu2->seen)
+               continue;
+             ipu2->seen = true;
+             maybe_subset = (e1 == ipu->incoming
+                             && ipu->incoming_count <= ipu2->incoming_count);
+             maybe_superset = ipu->incoming_count >= ipu2->incoming_count;
+             e3 = e1;
+             e4 = ipu2->incoming;
+             intersection = 0;
+             while ((maybe_subset || maybe_superset) && e3 && e4)
+               {
+                 if (e3->icu == e4->icu)
+                   {
+                     intersection++;
+                     e3 = e3->next;
+                     e4 = e4->next;
+                     continue;
+                   }
+                 if (e3->icu->idx < e4->icu->idx)
+                   {
+                     maybe_subset = false;
+                     e3 = e3->next;
+                     continue;
+                   }
+                 maybe_superset = false;
+                 e4 = e4->next;
+               }
+             if (e3)
+               maybe_subset = false;
+             if (e4)
+               maybe_superset = false;
+             if ((!maybe_superset && !maybe_subset) || intersection < 2)
+               continue;
+             if (maybe_superset && maybe_subset)
+               {
+                 /* If IPU and IPU2 have the same set of src nodes, then
+                    (if beneficial, with edge_cost != 0 always), merge
+                    IPU2 node into IPU, by removing all incoming edges
+                    of IPU2 and moving over all outgoing edges of IPU2
+                    to IPU.  */
+                 assert (ipu2->idx >= npus + ncus);
+                 size_inc = 0;
+                 if (edge_cost)
+                   size_dec = 13 + ipu2->incoming_count * edge_cost;
+                 else
+                   {
+                     size_dec = 13;
+                     if (ipu->cu && ipu->cu->cu_version == 2)
+                       {
+                         if (ptr_size > 4)
+                           size_inc = ipu2->outgoing_count * (ptr_size - 4);
+                         else
+                           size_dec += ipu2->outgoing_count * (4 - ptr_size);
+                       }
+                     for (e4 = ipu2->incoming; e4; e4 = e4->next)
+                       size_dec += (e4->icu->cu
+                                    && e4->icu->cu->cu_version == 2)
+                                   ? 1 + ptr_size : 5;
+                   }
+                 if (size_dec > size_inc)
+                   {
+                     struct import_cu **ipup;
+                     for (e4 = ipu2->incoming, ep = NULL; e4; e4 = e4->next)
+                       {
+                         remove_import_edges (&e4->icu->outgoing, &ipu2, 1,
+                                              false);
+                         e4->icu->outgoing_count--;
+                         ep = &e4->next;
+                       }
+                     *ep = edge_freelist;
+                     edge_freelist = ipu2->incoming;
+                     for (e4 = ipu2->outgoing; e4; e4 = e4->next)
+                       {
+                         for (ep = &e4->icu->incoming; *ep; ep = &(*ep)->next)
+                           if ((*ep)->icu->idx >= ipu->idx)
+                             break;
+                         assert ((*ep)->icu != ipu);
+                         if ((*ep)->icu == ipu2)
+                           (*ep)->icu = ipu;
+                         else
+                           {
+                             struct import_edge **ep2;
+                             for (ep2 = &(*ep)->next;
+                                  *ep2; ep2 = &(*ep2)->next)
+                               if ((*ep2)->icu == ipu2)
+                                 break;
+                             e3 = *ep2;
+                             *ep2 = e3->next;
+                             e3->next = *ep;
+                             *ep = e3;
+                             e3->icu = ipu;
+                           }
+                       }
+                     e3 = ipu->outgoing;
+                     ep = &ipu->outgoing;
+                     for (e4 = ipu2->outgoing; e3 && e4; )
+                       if (e3->icu->idx < e4->icu->idx)
+                         {
+                           *ep = e3;
+                           ep = &e3->next;
+                           e3 = e3->next;
+                         }
+                       else
+                         {
+                           assert (e3->icu != e4->icu);
+                           *ep = e4;
+                           ep = &e4->next;
+                           e4 = e4->next;
+                         }
+                     if (e3)
+                       *ep = e3;
+                     else if (e4)
+                       *ep = e4;
+                     else
+                       *ep = NULL;
+                     ipu->outgoing_count += ipu2->outgoing_count;
+                     size -= size_dec - size_inc;
+                     if (ipu->idx >= npus + ncus)
+                       ipup = &ipu->next;
+                     else
+                       ipup = &ipus[npus - 1]->next;
+                     while (*ipup != ipu2)
+                       ipup = &(*ipup)->next;
+                     *ipup = ipu2->next;
+                     ipu2->next = pu_freelist;
+                     pu_freelist = ipu2;
+                     continue;
+                   }
+               }
+             if (maybe_superset)
+               {
+                 ipusup = ipu;
+                 ipusub = ipu2;
+               }
+             else
+               {
+                 ipusub = ipu;
+                 ipusup = ipu2;
+               }
+             /* If IPUSUB's src set is a subset of IPUSUP's src set
+                and intersection is at least 2, remove edges from
+                IPUSUB's src set to IPUSUP node and instead add
+                an edge from IPUSUB to IPUSUP.  */
+             size_inc = 0;
+             if (edge_cost)
+               size_dec = (ipusub->incoming_count - 1) * edge_cost;
+             else
+               {
+                 size_inc = ipusub->cu && ipusub->cu->cu_version == 2
+                            ? 1 + ptr_size : 5;
+                 size_dec = 0;
+                 for (e3 = ipusub->incoming; e3; e3 = e3->next)
+                   size_dec += (e3->icu->cu
+                                && e3->icu->cu->cu_version == 2)
+                               ? 1 + ptr_size : 5;
+               }
+             if (size_dec > size_inc)
+               {
+                 for (e3 = ipusub->incoming, ep = &ipusup->incoming;
+                      e3; e3 = e3->next)
+                   {
+                     remove_import_edges (&e3->icu->outgoing, &ipusup, 1,
+                                          false);
+                     e3->icu->outgoing_count--;
+                     while ((*ep)->icu != e3->icu)
+                       ep = &(*ep)->next;
+                     e4 = *ep;
+                     *ep = e4->next;
+                     e4->next = edge_freelist;
+                     edge_freelist = e4;
+                   }
+                 for (ep = &ipusub->outgoing; *ep; ep = &(*ep)->next)
+                   if ((*ep)->icu->idx >= ipusup->idx)
+                     break;
+                 assert (*ep == NULL || (*ep)->icu != ipusup);
+                 e4 = edge_freelist;
+                 edge_freelist = edge_freelist->next;
+                 e4->icu = ipusup;
+                 e4->next = *ep;
+                 *ep = e4;
+                 ipusub->outgoing_count++;
+                 for (ep = &ipusup->incoming; *ep; ep = &(*ep)->next)
+                   if ((*ep)->icu->idx >= ipusub->idx)
+                     break;
+                 assert (*ep == NULL || (*ep)->icu != ipusub);
+                 e4 = edge_freelist;
+                 edge_freelist = edge_freelist->next;
+                 e4->icu = ipusup;
+                 e4->next = *ep;
+                 *ep = e4;
+                 ipusup->incoming_count -= ipusub->incoming_count - 1;
+                 size -= size_dec - size_inc;
+                 if (ipusup == ipu)
+                   break;
+               }
+           }
+       }
+      for (icu = ipu->next; icu; icu = icu->next)
+       icu->seen = false;
+    }
+  /* Create DW_TAG_partial_unit (and containing dw_cu structures).  */
+  cu_off = last_partial_cu->cu_offset + 1;
+  for (ipu = ipus[npus - 1]->next; ipu; ipu = ipu->next)
+    {
+      dw_die_ref die;
+      struct dw_cu *partial_cu
+       = (struct dw_cu *)
+         obstack_alloc (&ob, sizeof (struct dw_cu));
+      memset (partial_cu, '\0', sizeof (*partial_cu));
+      partial_cu->cu_offset = cu_off++;
+      partial_cu->cu_version = new_pu_version;
+      partial_cu->u1.cu_icu = ipu;
+      partial_cu->cu_next = last_partial_cu->cu_next;
+      last_partial_cu->cu_next = partial_cu;
+      last_partial_cu = partial_cu;
+      die = (dw_die_ref)
+           obstack_alloc (&ob, sizeof (struct dw_toplevel_die));
+      memset (die, '\0', sizeof (struct dw_toplevel_die));
+      die->die_toplevel = 1;
+      partial_cu->cu_die = die;
+      die->die_tag = DW_TAG_partial_unit;
+      die->die_offset = -1U;
+      die->die_cu = partial_cu;
+      die->die_size = 1;
+      ipu->cu = partial_cu;
+    }
+  /* Next add all needed DW_TAG_imported_unit DIEs.  */
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    {
+      struct import_edge *e;
+
+      icu = cu->u1.cu_icu;
+      if (icu == NULL)
+       continue;
+      for (e = icu->outgoing; e; e = e->next)
+       {
+         dw_die_ref *diep;
+         dw_die_ref die = (dw_die_ref)
+                          obstack_alloc (&ob,
+                                         sizeof (struct dw_toplevel_die));
+         memset (die, '\0', sizeof (*die));
+         die->die_toplevel = 1;
+         die->die_tag = DW_TAG_imported_unit;
+         die->die_offset = -1U;
+         die->die_cu = cu;
+         die_nextdup (die) = e->icu->cu->cu_die;
+         die->die_parent = die->die_cu->cu_die;
+         die->die_size = (cu->cu_version == 2 ? 1 + ptr_size : 5);
+         /* Put the new DW_TAG_imported_unit DIE after all typed DWARF
+            stack referenced base types and after all previously added
+            new DW_TAG_imported_unit DIEs.  */
+         for (diep = &die->die_parent->die_child;
+              *diep; diep = &(*diep)->die_sib)
+           if (!(*diep)->die_op_type_referenced
+               && ((*diep)->die_tag != DW_TAG_imported_unit
+                   || (*diep)->die_offset != -1U))
+             break;
+         die->die_sib = *diep;
+         *diep = die;
+       }
+    }
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    cu->u1.cu_icu = NULL;
+  obstack_free (&iob, NULL);
+  return 0;
+}
+
+/* Record abbreviations referenced from .debug_types section.
+   .debug_types sections aren't rewritten, so the abbrevs need
+   to stay as is.  */
+static int
+read_debug_types (DSO *dso)
+{
+  unsigned char *ptr, *endcu, *endsec;
+  uint32_t value;
+  htab_t abbrev;
+
+  ptr = debug_sections[DEBUG_TYPES].data;
+  if (ptr == NULL)
+    return 0;
+  endsec = ptr + debug_sections[DEBUG_TYPES].size;
+  while (ptr < endsec)
+    {
+      unsigned int cu_offset = ptr - debug_sections[DEBUG_TYPES].data;
+      int cu_version;
+      struct dw_cu *cu;
+
+      if (ptr + 23 > endsec)
+       {
+         error (0, 0, "%s: .debug_types CU header too small", dso->filename);
+         return 1;
+       }
+
+      endcu = ptr + 4;
+      endcu += read_32 (ptr);
+      if (endcu == ptr + 0xffffffff)
+       {
+         error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+         return 1;
+       }
+
+      if (endcu > endsec)
+       {
+         error (0, 0, "%s: .debug_types too small", dso->filename);
+         return 1;
+       }
+
+      cu_version = read_16 (ptr);
+      if (cu_version < 2 || cu_version > 4)
+       {
+         error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
+                cu_version);
+         return 1;
+       }
+
+      value = read_32 (ptr);
+      if (value >= debug_sections[DEBUG_ABBREV].size)
+       {
+         if (debug_sections[DEBUG_ABBREV].data == NULL)
+           error (0, 0, "%s: .debug_abbrev not present", dso->filename);
+         else
+           error (0, 0, "%s: DWARF CU abbrev offset too large",
+                  dso->filename);
+         return 1;
+       }
+
+      abbrev = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value,
+                           true);
+      if (abbrev == NULL)
+       return 1;
+
+      cu = (struct dw_cu *) obstack_alloc (&ob, sizeof (struct dw_cu));
+      memset (cu, '\0', sizeof (*cu));
+      cu->cu_new_abbrev = abbrev;
+      cu->cu_offset = cu_offset;
+      cu->cu_version = cu_version;
+      if (first_cu == NULL)
+       first_cu = last_cu = cu;
+      else
+       {
+         last_cu->cu_next = cu;
+         last_cu = cu;
+       }
+      ptr = endcu;
+    }
+
+  return 0;
+}
+
+/* If DIE is equal to ORIG, return DUP, otherwise if DIE is
+   a child of ORIG, return corresponding child in DUP's subtree,
+   or return NULL.  */
+static dw_die_ref
+die_find_dup (dw_die_ref orig, dw_die_ref dup, dw_die_ref die)
+{
+  dw_die_ref orig_child, dup_child;
+  if (orig == die)
+    return dup;
+  for (orig_child = orig->die_child, dup_child = dup->die_child;
+       orig_child;
+       orig_child = orig_child->die_sib, dup_child = dup_child->die_sib)
+    {
+      dw_die_ref ret = die_find_dup (orig_child, dup_child, die);
+      if (ret)
+       return ret;
+    }
+  return NULL;
+}
+
+/* Compute new abbreviations for DIE (with reference DIE REF).
+   T is a temporary buffer.  Fill in *NDIES - number of DIEs
+   in the tree, and record pairs of referrer/referree DIEs for
+   intra-CU references into obstack vector VEC.  */
+static int
+build_abbrevs_for_die (htab_t h, dw_die_ref die, dw_die_ref ref,
+                      struct abbrev_tag *t, unsigned int *ndies,
+                      struct obstack *vec)
+{
+  dw_die_ref child, ref_child, sib = NULL;
+  unsigned int i, j;
+  void **slot;
+
+  die->u.p2.die_new_abbrev = NULL;
+  die->u.p2.die_new_offset = 0;
+  die->u.p2.die_intracu_udata_size = 0;
+
+  if (die->die_remove)
+    return 0;
+  t->entry = 0;
+  t->tag = die->die_tag;
+  t->children = die->die_child != NULL;
+  t->op_type_referenced = false;
+  t->nusers = 1;
+  if (die->die_offset == -1U)
+    {
+      if (ref != NULL)
+       ;
+      else if (die_safe_nextdup (die) && die_dup (die_nextdup (die)) == die)
+       ref = die_nextdup (die);
+    }
+  else
+    ref = die;
+  if (die->die_child && die->die_sib)
+    {
+      for (sib = die->die_sib; sib; sib = sib->die_sib)
+       if (!sib->die_remove)
+         break;
+    }
+  if (ref)
+    {
+      unsigned char *ptr = debug_sections[DEBUG_INFO].data
+                          + ref->die_offset;
+      struct abbrev_tag *reft = ref->die_abbrev;
+
+      read_uleb128 (ptr);
+      /* No longer count the abbrev uleb128 size in die_size.
+        We'll add it back after determining the new abbrevs.  */
+      die->die_size -= ptr
+                      - (debug_sections[DEBUG_INFO].data + ref->die_offset);
+      for (i = 0; i < reft->nattr; i++)
+       switch (reft->attr[i].form)
+         {
+         case DW_FORM_ref1:
+         case DW_FORM_ref2:
+         case DW_FORM_ref4:
+         case DW_FORM_ref8:
+         case DW_FORM_ref_udata:
+         case DW_FORM_indirect:
+           i = -2U;
+           break;
+         default:
+           break;
+         }
+      if (i != -1U)
+       {
+         /* If there are no references, size stays the same
+            and no need to walk the actual attribute values.  */
+         for (i = 0; i < reft->nattr; i++)
+           {
+             t->attr[i].attr = reft->attr[i].attr;
+             t->attr[i].form = reft->attr[i].form;
+           }
+         t->nattr = reft->nattr;
+       }
+      else
+       {
+         /* Otherwise, we need to walk the actual attributes.  */
+         for (i = 0, j = 0; i < reft->nattr; ++i)
+           {
+             uint32_t form = reft->attr[i].form;
+             size_t len = 0;
+             dw_die_ref refd;
+             uint64_t value;
+             unsigned char *orig_ptr = ptr;
+
+             while (form == DW_FORM_indirect)
+               form = read_uleb128 (ptr);
+
+             switch (form)
+               {
+               case DW_FORM_ref_addr:
+                 ptr += ref->die_cu->cu_version == 2 ? ptr_size : 4;
+                 break;
+               case DW_FORM_addr:
+                 ptr += ptr_size;
+                 break;
+               case DW_FORM_flag_present:
+                 break;
+               case DW_FORM_flag:
+               case DW_FORM_data1:
+                 ++ptr;
+                 break;
+               case DW_FORM_data2:
+                 ptr += 2;
+                 break;
+               case DW_FORM_data4:
+               case DW_FORM_sec_offset:
+                 ptr += 4;
+                 break;
+               case DW_FORM_data8:
+               case DW_FORM_ref_sig8:
+                 ptr += 8;
+                 break;
+               case DW_FORM_sdata:
+               case DW_FORM_udata:
+                 read_uleb128 (ptr);
+                 break;
+               case DW_FORM_strp:
+                 ptr += 4;
+                 break;
+               case DW_FORM_string:
+                 ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+                 break;
+               case DW_FORM_indirect:
+                 abort ();
+               case DW_FORM_block1:
+                 len = *ptr++;
+                 break;
+               case DW_FORM_block2:
+                 len = read_16 (ptr);
+                 form = DW_FORM_block1;
+                 break;
+               case DW_FORM_block4:
+                 len = read_32 (ptr);
+                 form = DW_FORM_block1;
+                 break;
+               case DW_FORM_block:
+               case DW_FORM_exprloc:
+                 len = read_uleb128 (ptr);
+                 form = DW_FORM_block1;
+                 break;
+               case DW_FORM_ref1:
+               case DW_FORM_ref2:
+               case DW_FORM_ref4:
+               case DW_FORM_ref8:
+               case DW_FORM_ref_udata:
+                 switch (form)
+                   {
+                   case DW_FORM_ref1: value = read_8 (ptr); break;
+                   case DW_FORM_ref2: value = read_16 (ptr); break;
+                   case DW_FORM_ref4: value = read_32 (ptr); break;
+                   case DW_FORM_ref8: value = read_64 (ptr); break;
+                   case DW_FORM_ref_udata: value = read_uleb128 (ptr); break;
+                   default: abort ();
+                   }
+                 die->die_size -= ptr - orig_ptr;
+                 if (reft->attr[i].attr == DW_AT_sibling)
+                   {
+                     if (sib == NULL)
+                       continue;
+                     form = DW_FORM_ref4;
+                     refd = sib;
+                   }
+                 else
+                   {
+                     dw_die_ref refdt;
+                     refd = off_htab_lookup (ref->die_cu->cu_offset + value);
+                     assert (refd != NULL);
+                     refdt = refd;
+                     while (refdt->die_toplevel == 0)
+                       refdt = refdt->die_parent;
+                     if (die_dup (refdt) && refdt->die_op_type_referenced)
+                       {
+                         if (die->die_cu == refd->die_cu)
+                           form = DW_FORM_ref4;
+                         else if (die->die_cu == die_dup (refdt)->die_cu)
+                           {
+                             form = DW_FORM_ref4;
+                             refd = die_find_dup (refdt, die_dup (refdt),
+                                                  refd);
+                           }
+                         else
+                           form = DW_FORM_ref_addr;
+                       }
+                     else
+                       {
+                         if (die_dup (refdt))
+                           refd = die_find_dup (refdt, die_dup (refdt), refd);
+                         if (die->die_cu == refd->die_cu)
+                           form = DW_FORM_ref4;
+                         else
+                           form = DW_FORM_ref_addr;
+                       }
+                   }
+                 if (form == DW_FORM_ref_addr)
+                   die->die_size += die->die_cu->cu_version == 2 ? ptr_size : 4;
+                 else
+                   {
+                     obstack_ptr_grow (vec, die);
+                     obstack_ptr_grow (vec, refd);
+                   }
+                 t->attr[j].attr = reft->attr[i].attr;
+                 t->attr[j++].form = form;
+                 continue;
+               default:
+                 abort ();
+               }
+
+             if (form == DW_FORM_block1)
+               ptr += len;
+             t->attr[j].attr = reft->attr[i].attr;
+             t->attr[j++].form = reft->attr[i].form;
+           }
+         t->nattr = j;
+       }
+    }
+  else
+    switch (die->die_tag)
+      {
+      case DW_TAG_partial_unit:
+       t->nattr = 0;
+       die->die_size = 0;
+       if (die_nextdup (die) == NULL)
+         break;
+       if (die_nextdup (die)->die_cu->cu_nfiles)
+         {
+           t->attr[0].attr = DW_AT_stmt_list;
+           t->attr[0].form = die->die_cu->cu_version < 4
+                             ? DW_FORM_sec_offset : DW_FORM_data4;
+           die->die_size += 4;
+           t->nattr++;
+         }
+       if (die_nextdup (die)->die_cu->cu_comp_dir)
+         {
+           enum dwarf_form form;
+           unsigned char *ptr = get_AT (die_nextdup (die),
+                                        DW_AT_comp_dir, &form);
+           assert (ptr && (form == DW_FORM_string || form == DW_FORM_strp));
+           t->attr[t->nattr].attr = DW_AT_comp_dir;
+           t->attr[t->nattr].form = form;
+           if (form == DW_FORM_strp)
+             die->die_size += 4;
+           else
+             die->die_size
+               = strlen (die_nextdup (die)->die_cu->cu_comp_dir) + 1;
+           t->nattr++;
+         }
+       break;
+      case DW_TAG_namespace:
+      case DW_TAG_module:
+       {
+         enum dwarf_form form;
+         unsigned char *ptr = get_AT (die_nextdup (die), DW_AT_name, &form);
+         assert (ptr && (form == DW_FORM_string || form == DW_FORM_strp));
+         t->attr[0].attr = DW_AT_name;
+         t->attr[0].form = form;
+         if (form == DW_FORM_strp)
+           die->die_size = 4;
+         else
+           die->die_size = strlen ((char *) ptr) + 1;
+         t->nattr = 1;
+         if (sib)
+           {
+             t->attr[1].attr = DW_AT_sibling;
+             t->attr[1].form = DW_FORM_ref4;
+             obstack_ptr_grow (vec, die);
+             obstack_ptr_grow (vec, sib);
+             t->nattr++;
+           }
+         break;
+       }
+      case DW_TAG_imported_unit:
+       t->attr[0].attr = DW_AT_import;
+       t->attr[0].form = DW_FORM_ref_addr;
+       t->nattr = 1;
+       die->die_size = die->die_cu->cu_version == 2 ? ptr_size : 4;
+       break;
+      default:
+       abort ();
+      }
+  compute_abbrev_hash (t);
+  slot = htab_find_slot_with_hash (h, t, t->hash, INSERT);
+  if (slot == NULL)
+    {
+      error (0, ENOMEM, "Could not compute .debug_abbrev");
+      return 1;
+    }
+  if (*slot)
+    {
+      ((struct abbrev_tag *)*slot)->nusers++;
+      die->u.p2.die_new_abbrev = (struct abbrev_tag *)*slot;
+    }
+  else
+    {
+      struct abbrev_tag *newt
+       = xmalloc (sizeof (*newt) + t->nattr * sizeof (struct abbrev_attr));
+      memcpy (newt, t, sizeof (*newt)
+                      + t->nattr * sizeof (struct abbrev_attr));
+      *slot = newt;
+      die->u.p2.die_new_abbrev = newt;
+    }
+  (*ndies)++;
+  if (ref != NULL && ref != die)
+    {
+      for (child = die->die_child, ref_child = ref->die_child;
+          child; child = child->die_sib, ref_child = ref_child->die_sib)
+       if (build_abbrevs_for_die (h, child, ref_child, t, ndies, vec))
+         return 1;
+    }
+  else
+    for (child = die->die_child; child; child = child->die_sib)
+      if (build_abbrevs_for_die (h, child, NULL, t, ndies, vec))
+       return 1;
+  return 0;
+}
+
+/* Build new abbreviations for CU.  T, NDIES and VEC arguments like
+   for build_abbrevs_for_die.  */
+static int
+build_abbrevs (struct dw_cu *cu, struct abbrev_tag *t, unsigned int *ndies,
+              struct obstack *vec)
+{
+  htab_t h = htab_try_create (50, abbrev_hash, abbrev_eq2, NULL);
+
+  if (h == NULL)
+    {
+      error (0, ENOMEM, "Could not compute .debug_abbrev");
+      return 1;
+    }
+
+  if (build_abbrevs_for_die (h, cu->cu_die, NULL, t, ndies, vec))
+    return 1;
+
+  cu->cu_new_abbrev = h;
+  return 0;
+}
+
+/* Helper to record all abbrevs from the hash table into ob obstack
+   vector.  Called through htab_traverse.  */
+static int
+list_abbrevs (void **slot, void *info __attribute__((unused)))
+{
+  obstack_ptr_grow (&ob, *slot);
+  return 1;
+}
+
+/* Helper function to find abbrev with highest abbrev number,
+   called through htab_traverse.  */
+static int
+find_max_abbrev_entry (void **slot, void *info)
+{
+  struct abbrev_tag *t = (struct abbrev_tag *) *slot;
+  unsigned int *largest = (unsigned int *) info;
+  if (t->entry > *largest)
+    *largest = t->entry;
+  return 1;
+}
+
+/* Comparison function for abbreviations.  Used for CUs that
+   need 128 or more abbreviations.  Use lowest numbers (i.e. sort earlier)
+   abbrevs used for typed DWARF stack referenced DIEs, then sort
+   by decreasing number of users (abbrev numbers are uleb128 encoded,
+   the bigger number of them that can be 1 byte encoded the better).  */
+static int
+abbrev_cmp (const void *p, const void *q)
+{
+  struct abbrev_tag *t1 = *(struct abbrev_tag **)p;
+  struct abbrev_tag *t2 = *(struct abbrev_tag **)q;
+  unsigned int i;
+
+  if (t1->op_type_referenced && !t2->op_type_referenced)
+    return -1;
+  if (!t1->op_type_referenced && t2->op_type_referenced)
+    return 1;
+  if (t1->nusers > t2->nusers)
+    return -1;
+  if (t1->nusers < t2->nusers)
+    return 1;
+  /* The rest just so that we have a stable sort.  */
+  if (t1->tag < t2->tag)
+    return -1;
+  if (t1->tag > t2->tag)
+    return 1;
+  if (t1->nattr < t2->nattr)
+    return -1;
+  if (t1->nattr > t2->nattr)
+    return 1;
+  if (t1->children && !t2->children)
+    return -1;
+  if (!t1->children && t2->children)
+    return 1;
+  for (i = 0; i < t1->nattr; i++)
+    {
+      if (t1->attr[i].attr < t2->attr[i].attr)
+       return -1;
+      if (t1->attr[i].attr > t2->attr[i].attr)
+       return 1;
+      if (t1->attr[i].form < t2->attr[i].form)
+       return -1;
+      if (t1->attr[i].form > t2->attr[i].form)
+       return 1;
+    }
+  return 0;
+}
+
+/* Return number of bytes needed to encode VAL using
+   uleb128.  */
+static unsigned int
+size_of_uleb128 (uint64_t val)
+{
+  unsigned int size;
+  for (size = 1; (val >>= 7) != 0; size++)
+    ;
+  return size;
+}
+
+/* First phase of computation of u.p2.die_new_offset and
+   new CU sizes.  */
+static unsigned int
+init_new_die_offsets (dw_die_ref die, unsigned int off,
+                     unsigned int intracusize)
+{
+  dw_die_ref child;
+  unsigned int i;
+  struct abbrev_tag *t = die->u.p2.die_new_abbrev;
+  if (die->die_remove)
+    return off;
+  die->u.p2.die_new_offset = off;
+  die->die_size += size_of_uleb128 (die->u.p2.die_new_abbrev->entry);
+  die->u.p2.die_intracu_udata_size = 0;
+  for (i = 0; i < t->nattr; ++i)
+    if (t->attr[i].form == DW_FORM_ref4)
+      die->u.p2.die_intracu_udata_size += intracusize;
+  die->die_size += die->u.p2.die_intracu_udata_size;
+  off += die->die_size;
+  for (child = die->die_child; child; child = child->die_sib)
+    off = init_new_die_offsets (child, off, intracusize);
+  if (die->die_child)
+    off++;
+  return off;
+}
+
+/* Second phase of computation of u.p2.die_new_offset and
+   new CU sizes.  This step is called possibly many times,
+   for deciding if DW_FORM_ref_udata is worthwhile.
+   init_new_die_offsets starts with assuming each uleb128 will
+   need maximum number of bytes for the CU of the given size,
+   each new invocation of this function (except the last)
+   will shrink one or more uleb128s.  Each shrinking can create
+   new opportunities to shrink other uleb128s.  */
+static unsigned int
+update_new_die_offsets (dw_die_ref die, unsigned int off,
+                       dw_die_ref **intracuvec)
+{
+  dw_die_ref child;
+  if (die->die_remove)
+    return off;
+  assert (off <= die->u.p2.die_new_offset);
+  die->u.p2.die_new_offset = off;
+  if ((*intracuvec)[0] == die)
+    {
+      unsigned int intracu_udata_size = 0;
+      assert (die->u.p2.die_intracu_udata_size);
+      while ((*intracuvec)[0] == die)
+       {
+         intracu_udata_size
+           += size_of_uleb128 ((*intracuvec)[1]->u.p2.die_new_offset);
+         *intracuvec += 2;
+       }
+      assert (die->u.p2.die_intracu_udata_size >= intracu_udata_size);
+      die->die_size -= die->u.p2.die_intracu_udata_size - intracu_udata_size;
+      die->u.p2.die_intracu_udata_size = intracu_udata_size;
+    }
+  else
+    assert (die->u.p2.die_intracu_udata_size == 0);
+  off += die->die_size;
+  for (child = die->die_child; child; child = child->die_sib)
+    off = update_new_die_offsets (child, off, intracuvec);
+  if (die->die_child)
+    off++;
+  return off;
+}
+
+/* Final phase of computation of u.p2.die_new_offset.  Called when already
+   decided what intra-CU form will be used.  Can return -1U if
+   a problem is detected and the tool should give up.  */
+static unsigned int
+finalize_new_die_offsets (dw_die_ref die, unsigned int off,
+                         unsigned int intracusize, dw_die_ref **intracuvec)
+{
+  dw_die_ref child;
+  if (die->die_remove)
+    return off;
+  die->u.p2.die_new_offset = off;
+  /* As we aren't adjusting sizes of exprloc, if in the new layout
+     a DIE referenced through DW_OP_call2 is placed after 64K into
+     the CU, punt.  */
+  if (die->die_op_call2_referenced && off >= 65536)
+    return -1U;
+  /* Similarly punt if
+     DW_OP_GNU_{{regval,const,deref}_type,convert,reinterpret}
+     references a DIE that needs more uleb128 bytes to encode
+     the new offset compared to uleb128 bytes to encode the old offset.
+     GCC emits DW_TAG_base_type dies referenced that way at the
+     beginning of the CU and we try to preserve that, so this shouldn't
+     trigger for GCC generated code.  */
+  if (die->die_op_type_referenced
+      && size_of_uleb128 (off)
+        > size_of_uleb128 (die->die_offset - die->die_cu->cu_offset))
+    return -1U;
+  if ((*intracuvec)[0] == die)
+    {
+      unsigned int intracu_udata_size = 0;
+      assert (die->u.p2.die_intracu_udata_size);
+      while ((*intracuvec)[0] == die)
+       {
+         intracu_udata_size += intracusize;
+         *intracuvec += 2;
+       }
+      if (intracusize != 0)
+       {
+         die->die_size
+           -= die->u.p2.die_intracu_udata_size - intracu_udata_size;
+         die->u.p2.die_intracu_udata_size = intracu_udata_size;
+       }
+    }
+  else
+    assert (die->u.p2.die_intracu_udata_size == 0);
+  off += die->die_size;
+  for (child = die->die_child; child; child = child->die_sib)
+    {
+      off = finalize_new_die_offsets (child, off, intracusize, intracuvec);
+      if (off == -1U)
+       return off;
+    }
+  if (die->die_child)
+    off++;
+  return off;
+}
+
+/* Comparison function, called through qsort, to sort CUs
+   by increasing number of needed new abbreviations.  */
+static int
+cu_abbrev_cmp (const void *p, const void *q)
+{
+  struct dw_cu *cu1 = *(struct dw_cu **)p;
+  struct dw_cu *cu2 = *(struct dw_cu **)q;
+  unsigned int nabbrevs1 = htab_elements (cu1->cu_new_abbrev);
+  unsigned int nabbrevs2 = htab_elements (cu2->cu_new_abbrev);
+
+  if (nabbrevs1 < nabbrevs2)
+    return -1;
+  if (nabbrevs1 > nabbrevs2)
+    return 1;
+  /* The rest is just to get stable sort.  */
+  if (cu1->cu_abbrev != NULL && cu2->cu_abbrev == NULL)
+    return -1;
+  if (cu1->cu_abbrev == NULL && cu2->cu_abbrev != NULL)
+    return 1;
+  if (cu1->cu_offset < cu2->cu_offset)
+    return -1;
+  if (cu1->cu_offset > cu2->cu_offset)
+    return 1;
+  return 0;
+}
+
+/* Compute new abbreviations for all CUs, size the new
+   .debug_abbrev section and all new .debug_info CUs.
+   Store the size of new .debug_info section to *TOTAL_SIZEP
+   and size of new .debug_abbrev section into *ABBREV_SIZEP.  */
+static int
+compute_abbrevs (DSO *dso, unsigned long *total_sizep,
+                unsigned long *abbrev_sizep)
+{
+  unsigned long total_size = 0, abbrev_size = 0;
+  struct dw_cu *cu, **cuarr;
+  struct abbrev_tag *t
+    = xmalloc (sizeof (*t) + (max_nattr + 4) * sizeof (struct abbrev_attr));
+  unsigned int ncus, nlargeabbrevs = 0, i, laststart;
+  struct obstack vec;
+
+  obstack_init (&vec);
+  for (cu = first_cu, ncus = 0; cu; cu = cu->cu_next, ncus++)
+    {
+      unsigned int intracu, ndies = 0, tagsize = 0, nchildren = 0;
+      unsigned int nabbrevs, diesize, cusize, off, intracusize;
+      struct abbrev_tag **arr;
+      dw_die_ref *intracuarr, *intracuvec;
+      enum dwarf_form intracuform = DW_FORM_ref4;
+      dw_die_ref child, *lastotr, child_next, *last;
+
+      if (cu->cu_die == NULL)
+       {
+         if (htab_elements (cu->cu_new_abbrev) >= 128)
+           nlargeabbrevs++;
+         cu->u2.cu_largest_entry = 0;
+         htab_traverse (cu->cu_new_abbrev, find_max_abbrev_entry,
+                        &cu->u2.cu_largest_entry);
+         continue;
+       }
+      if (build_abbrevs (cu, t, &ndies, &vec))
+       {
+         obstack_free (&vec, NULL);
+         free (t);
+         return 1;
+       }
+      nabbrevs = htab_elements (cu->cu_new_abbrev);
+      htab_traverse (cu->cu_new_abbrev, list_abbrevs, NULL);
+      assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *));
+      arr = (struct abbrev_tag **) obstack_finish (&ob);
+      intracu = obstack_object_size (&vec) / sizeof (void *) / 2;
+      obstack_ptr_grow (&vec, NULL);
+      intracuarr = (dw_die_ref *) obstack_finish (&vec);
+      if (nabbrevs >= 128)
+       {
+         unsigned int limit, uleb128_size;
+
+         for (child = cu->cu_die->die_child; child; child = child->die_sib)
+           if (child->die_op_type_referenced)
+             {
+               child->u.p2.die_new_abbrev->op_type_referenced = 1;
+               /* If the old offset was close to uleb128 boundary, ensure
+                  that DW_TAG_compile_unit gets small abbrev number
+                  as well.  */
+               if (size_of_uleb128 (child->die_offset - cu->cu_offset)
+                   < size_of_uleb128 (child->die_offset - cu->cu_offset + 1))
+                 cu->cu_die->u.p2.die_new_abbrev->op_type_referenced = 1;
+             }
+         qsort (arr, nabbrevs, sizeof (*arr), abbrev_cmp);
+         for (i = 0, limit = 128, uleb128_size = 1; i < nabbrevs; i++)
+           {
+             if (i + 1 == limit)
+               {
+                 limit <<= 7;
+                 uleb128_size++;
+               }
+             arr[i]->entry = i + 1;
+             tagsize += arr[i]->nusers * uleb128_size;
+             if (arr[i]->children)
+               nchildren += arr[i]->nusers;
+           }
+         nlargeabbrevs++;
+       }
+      else
+       {
+         tagsize = ndies;
+         for (i = 0; i < nabbrevs; i++)
+           {
+             arr[i]->entry = i + 1;
+             if (arr[i]->children)
+               nchildren += arr[i]->nusers;
+           }
+       }
+
+      /* Move all base types with die_op_type_reference
+        to front, to increase the likelyhood that the offset
+        will fit.  */
+      for (last = &cu->cu_die->die_child, lastotr = last, child = *last;
+          child; child = child_next)
+       {
+         child_next = child->die_sib;
+         if (child->die_op_type_referenced)
+           {
+             if (lastotr != last)
+               {
+                 child->die_sib = *lastotr;
+                 *lastotr = child;
+                 lastotr = &child->die_sib;
+                 *last = child_next;
+                 continue;
+               }
+             lastotr = &child->die_sib;
+           }
+         last = &child->die_sib;
+       }
+
+      cu->u2.cu_largest_entry = nabbrevs;
+      diesize = calc_sizes (cu->cu_die);
+      cusize = 11 + tagsize + diesize + nchildren;
+      intracusize = size_of_uleb128 (cusize + intracu);
+      do
+       {
+         i = size_of_uleb128 (cusize + intracu * intracusize);
+         if (i == intracusize)
+           break;
+         intracusize = i;
+       }
+      while (1);
+      off = init_new_die_offsets (cu->cu_die, 11, intracusize);
+      do
+       {
+         intracuvec = intracuarr;
+         i = update_new_die_offsets (cu->cu_die, 11, &intracuvec);
+         assert (*intracuvec == NULL);
+         if (i == off)
+           break;
+         assert (i < off);
+         off = i;
+       }
+      while (1);
+      if (cusize + intracu <= 256)
+       {
+         intracuform = DW_FORM_ref1;
+         intracusize = 1;
+         cusize += intracu;
+       }
+      else if (cusize + intracu * 2 <= 65536)
+       {
+         intracuform = DW_FORM_ref2;
+         intracusize = 2;
+         cusize += intracu * 2;
+       }
+      else
+       {
+         cusize += intracu * 4;
+         intracusize = 4;
+       }
+      if (off <= cusize)
+       {
+         intracuform = DW_FORM_ref_udata;
+         intracusize = 0;
+         cusize = off;
+       }
+
+      intracuvec = intracuarr;
+      off = finalize_new_die_offsets (cu->cu_die, 11, intracusize, &intracuvec);
+      if (off == -1U)
+       {
+         error (0, 0, "%s: DW_OP_call2 or typed DWARF stack referenced DIE"
+                      " layed out at too big offset", dso->filename);
+         obstack_free (&vec, NULL);
+         free (t);
+         return 1;
+       }
+      assert (*intracuvec == NULL && off == cusize);
+
+      if (intracuform != DW_FORM_ref4)
+       {
+         unsigned int j;
+         htab_empty (cu->cu_new_abbrev);
+         for (i = 0; i < nabbrevs; i++)
+           {
+             void **slot;
+             for (j = 0; j < arr[i]->nattr; j++)
+               if (arr[i]->attr[j].form == DW_FORM_ref4)
+                 arr[i]->attr[j].form = intracuform;
+             compute_abbrev_hash (arr[i]);
+             slot = htab_find_slot_with_hash (cu->cu_new_abbrev, arr[i],
+                                              arr[i]->hash, INSERT);
+             if (slot == NULL)
+               {
+                 obstack_free (&ob, (void *) arr);
+                 obstack_free (&vec, NULL);
+                 free (t);
+                 error (0, ENOMEM, "Could not compute .debug_abbrev");
+                 return 1;
+               }
+             assert (slot != NULL && *slot == NULL);
+             *slot = arr[i];
+           }
+       }
+      obstack_free (&ob, (void *) arr);
+      obstack_free (&vec, (void *) intracuarr);
+      cu->cu_new_offset = total_size;
+      total_size += cusize;
+    }
+  obstack_free (&vec, NULL);
+  free (t);
+  cuarr = (struct dw_cu **) xmalloc (ncus * sizeof (struct dw_cu *));
+  for (cu = first_cu, i = 0; cu; cu = cu->cu_next, i++)
+    cuarr[i] = cu;
+  qsort (cuarr, ncus, sizeof (struct dw_cu *), cu_abbrev_cmp);
+  /* For CUs with < 128 abbrevs, try to see if either all of the
+     abbrevs are at < 128 positions in >= 128 abbrev CUs, or
+     can be merged with some other small abbrev table to form
+     a < 128 abbrev table.  */
+  laststart = ncus - nlargeabbrevs;
+  for (i = ncus - 1; i != -1U; i--)
+    {
+      struct abbrev_tag **arr;
+      unsigned int nabbrevs, j, k, nattempts;
+
+      if (cuarr[i]->u1.cu_new_abbrev_owner != NULL)
+       continue;
+      nabbrevs = htab_elements (cuarr[i]->cu_new_abbrev);
+      htab_traverse (cuarr[i]->cu_new_abbrev, list_abbrevs, NULL);
+      assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *));
+      arr = (struct abbrev_tag **) obstack_finish (&ob);
+      if (nabbrevs >= 128)
+       {
+         nattempts = 0;
+         for (j = i + 1; j < ncus; j++)
+           {
+             unsigned int entry;
+             if (cuarr[j]->u1.cu_new_abbrev_owner)
+               continue;
+             if (++nattempts == 100)
+               break;
+             entry = cuarr[j]->u2.cu_largest_entry;
+             for (k = 0; k < nabbrevs; k++)
+               {
+                 struct abbrev_tag *t
+                   = htab_find_with_hash (cuarr[j]->cu_new_abbrev,
+                                          arr[k], arr[k]->hash);
+                 /* We aren't rewriting .debug_types section, merely
+                    adjusting .debug_abbrev offsets into it, so the
+                    assigned entry values for .debug_types section
+                    can't change.  */
+                 if (cuarr[i]->cu_die == NULL)
+                   {
+                     if (t == NULL || t->entry != arr[k]->entry)
+                       break;
+                   }
+                 else if (t == NULL)
+                   {
+                     ++entry;
+                     if (size_of_uleb128 (entry)
+                         != size_of_uleb128 (arr[k]->entry))
+                       break;
+                   }
+                 else if (size_of_uleb128 (t->entry)
+                          != size_of_uleb128 (arr[k]->entry))
+                   break;
+               }
+             if (k != nabbrevs)
+               continue;
+             entry = cuarr[j]->u2.cu_largest_entry;
+             for (k = 0; k < nabbrevs; k++)
+               {
+                 void **slot
+                   = htab_find_slot_with_hash (cuarr[j]->cu_new_abbrev,
+                                               arr[k], arr[k]->hash,
+                                               INSERT);
+                 if (slot == NULL)
+                   {
+                     obstack_free (&ob, (void *) arr);
+                     free (cuarr);
+                     error (0, ENOMEM, "Could not compute .debug_abbrev");
+                     return 1;
+                   }
+                 if (*slot != NULL)
+                   arr[k]->entry = ((struct abbrev_tag *) *slot)->entry;
+                 else
+                   {
+                     struct abbrev_tag *newt
+                       = xmalloc (sizeof (*newt)
+                                  + arr[k]->nattr
+                                    * sizeof (struct abbrev_attr));
+                     arr[k]->entry = ++entry;
+                     memcpy (newt, arr[k],
+                             sizeof (*newt)
+                             + arr[k]->nattr * sizeof (struct abbrev_attr));
+                     *slot = newt;
+                   }
+               }
+             cuarr[j]->u2.cu_largest_entry = entry;
+             cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j];
+             break;
+           }
+         obstack_free (&ob, (void *) arr);
+         continue;
+       }
+      /* Don't search all CUs, that might be too expensive.  So just search
+        100 of >= 128 abbrev tables, if there are more than 100, different
+        set each time.  We are looking for a full match (i.e. that
+        cuarr[i] abbrevs are a subset of cuarr[j] abbrevs, and all of them
+        are in the low positions.  */
+      for (j = laststart, nattempts = -1U; nlargeabbrevs; j++)
+       {
+         if (j == ncus)
+           j -= nlargeabbrevs;
+         if (nattempts != -1U && j == laststart)
+           break;
+         if (nattempts == -1U)
+           nattempts = 0;
+         if (cuarr[j]->u1.cu_new_abbrev_owner)
+           continue;
+         if (++nattempts == 100)
+           break;
+         for (k = 0; k < nabbrevs; k++)
+           {
+             struct abbrev_tag *t
+               = htab_find_with_hash (cuarr[j]->cu_new_abbrev,
+                                      arr[k], arr[k]->hash);
+             if (t == NULL || t->entry >= 128)
+               break;
+             /* We aren't rewriting .debug_types section, merely adjusting
+                .debug_abbrev offsets into it, so the assigned entry
+                values for .debug_types section can't change.  */
+             if (cuarr[i]->cu_die == NULL && t->entry != arr[k]->entry)
+               break;
+           }
+         if (k == nabbrevs)
+           {
+             for (k = 0; k < nabbrevs; k++)
+               {
+                 struct abbrev_tag *t
+                   = htab_find_with_hash (cuarr[j]->cu_new_abbrev,
+                                          arr[k], arr[k]->hash);
+                 arr[k]->entry = t->entry;
+               }
+             cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j];
+             break;
+           }
+       }
+      if (nlargeabbrevs > 100)
+       laststart = j;
+      if (cuarr[i]->u1.cu_new_abbrev_owner == NULL)
+       {
+         unsigned int maxdups = 0, maxdupidx = 0;
+         /* Next search up to 100 of small abbrev CUs, looking
+            for best match.  */
+         nattempts = 0;
+         for (j = i + 1; j < ncus - nlargeabbrevs; j++)
+           {
+             unsigned int curdups = 0;
+             if (cuarr[j]->u1.cu_new_abbrev_owner)
+               continue;
+             if (++nattempts == 100)
+               break;
+             for (k = 0; k < nabbrevs; k++)
+               {
+                 struct abbrev_tag *t
+                   = htab_find_with_hash (cuarr[j]->cu_new_abbrev,
+                                          arr[k], arr[k]->hash);
+                 if (t != NULL)
+                   curdups++;
+                 /* We aren't rewriting .debug_types section, merely adjusting
+                    .debug_abbrev offsets into it, so the assigned entry
+                    values for .debug_types section can't change.  */
+                 if (cuarr[i]->cu_die == NULL
+                     && (t == NULL || t->entry != arr[k]->entry))
+                   {
+                     curdups = 0;
+                     break;
+                   }
+               }
+             if (curdups > maxdups
+                 && cuarr[j]->u2.cu_largest_entry - curdups + nabbrevs < 128)
+               {
+                 maxdups = curdups;
+                 maxdupidx = j;
+                 if (maxdups == nabbrevs)
+                   break;
+               }
+           }
+         if (maxdups)
+           {
+             unsigned int entry = cuarr[maxdupidx]->u2.cu_largest_entry;
+             j = maxdupidx;
+             for (k = 0; k < nabbrevs; k++)
+               {
+                 void **slot
+                   = htab_find_slot_with_hash (cuarr[j]->cu_new_abbrev,
+                                               arr[k], arr[k]->hash,
+                                               INSERT);
+                 if (slot == NULL)
+                   {
+                     obstack_free (&ob, (void *) arr);
+                     free (cuarr);
+                     error (0, ENOMEM, "Could not compute .debug_abbrev");
+                     return 1;
+                   }
+                 if (*slot != NULL)
+                   arr[k]->entry = ((struct abbrev_tag *) *slot)->entry;
+                 else
+                   {
+                     struct abbrev_tag *newt
+                       = xmalloc (sizeof (*newt)
+                                  + arr[k]->nattr
+                                    * sizeof (struct abbrev_attr));
+                     arr[k]->entry = ++entry;
+                     memcpy (newt, arr[k],
+                             sizeof (*newt)
+                             + arr[k]->nattr * sizeof (struct abbrev_attr));
+                     *slot = newt;
+                   }
+               }
+             cuarr[j]->u2.cu_largest_entry = entry;
+             cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j];
+           }
+       }
+      obstack_free (&ob, (void *) arr);
+    }
+  free (cuarr);
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    {
+      struct abbrev_tag **arr;
+      unsigned int nabbrevs, j;
+
+      if (cu->u1.cu_new_abbrev_owner != NULL)
+       {
+         cu->u2.cu_new_abbrev_offset = -1U;
+         continue;
+       }
+      cu->u2.cu_new_abbrev_offset = abbrev_size;
+      nabbrevs = htab_elements (cu->cu_new_abbrev);
+      htab_traverse (cu->cu_new_abbrev, list_abbrevs, NULL);
+      assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *));
+      arr = (struct abbrev_tag **) obstack_finish (&ob);
+      for (i = 0; i < nabbrevs; i++)
+       {
+         abbrev_size += size_of_uleb128 (arr[i]->entry);
+         abbrev_size += size_of_uleb128 (arr[i]->tag);
+         abbrev_size += 1;
+         for (j = 0; j < arr[i]->nattr; j++)
+           {
+             abbrev_size += size_of_uleb128 (arr[i]->attr[j].attr);
+             abbrev_size += size_of_uleb128 (arr[i]->attr[j].form);
+           }
+         abbrev_size += 2;
+       }
+      abbrev_size += 1;
+      obstack_free (&ob, (void *) arr);
+    }
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    if (cu->u2.cu_new_abbrev_offset == -1U)
+      {
+       struct dw_cu *owner = cu;
+       unsigned int cu_new_abbrev_offset;
+       while (owner->u1.cu_new_abbrev_owner != NULL)
+         owner = owner->u1.cu_new_abbrev_owner;
+       cu_new_abbrev_offset = owner->u2.cu_new_abbrev_offset;
+       owner = cu;
+       while (owner->u1.cu_new_abbrev_owner != NULL)
+         {
+           owner->u2.cu_new_abbrev_offset = cu_new_abbrev_offset;
+           owner = owner->u1.cu_new_abbrev_owner;
+         }
+      }
+  *total_sizep = total_size;
+  *abbrev_sizep = abbrev_size;
+  return 0;
+}
+
+/* Comparison function, sort abbreviations by increasing
+   entry value.  */
+static int
+abbrev_entry_cmp (const void *p, const void *q)
+{
+  struct abbrev_tag *t1 = *(struct abbrev_tag **)p;
+  struct abbrev_tag *t2 = *(struct abbrev_tag **)q;
+
+  if (t1->entry < t2->entry)
+    return -1;
+  if (t1->entry > t2->entry)
+    return 1;
+  return 0;
+}
+
+/* Construct the new .debug_abbrev section of size
+   ABBREV_SIZE in malloced memory, return it.  */
+static unsigned char *
+write_abbrev (unsigned long abbrev_size)
+{
+  struct dw_cu *cu;
+  unsigned char *abbrev = xmalloc (abbrev_size);
+  unsigned char *ptr = abbrev;
+
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    {
+      struct abbrev_tag **arr;
+      unsigned int nabbrevs, i, j;
+
+      if (cu->u1.cu_new_abbrev_owner != NULL)
+       continue;
+      nabbrevs = htab_elements (cu->cu_new_abbrev);
+      htab_traverse (cu->cu_new_abbrev, list_abbrevs, NULL);
+      assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *));
+      arr = (struct abbrev_tag **) obstack_finish (&ob);
+      qsort (arr, nabbrevs, sizeof (*arr), abbrev_entry_cmp);
+      for (i = 0; i < nabbrevs; i++)
+       {
+         write_uleb128 (ptr, arr[i]->entry);
+         write_uleb128 (ptr, arr[i]->tag);
+         *ptr++ = arr[i]->children ? DW_CHILDREN_yes : DW_CHILDREN_no;
+         for (j = 0; j < arr[i]->nattr; j++)
+           {
+             write_uleb128 (ptr, arr[i]->attr[j].attr);
+             write_uleb128 (ptr, arr[i]->attr[j].form);
+           }
+         *ptr++ = 0;
+         *ptr++ = 0;
+       }
+      *ptr++ = 0;
+      obstack_free (&ob, (void *) arr);
+    }
+  assert (abbrev + abbrev_size == ptr);
+  return abbrev;
+}
+
+/* Adjust DWARF expression starting at PTR, LEN bytes long, referenced by
+   DIE, with REF being the original DIE.  */
+static void
+adjust_exprloc (dw_die_ref die, dw_die_ref ref, unsigned char *ptr, size_t len)
+{
+  unsigned char *end = ptr + len, *orig_ptr = NULL;
+  unsigned char op;
+  uint32_t leni;
+  GElf_Addr addr;
+  dw_die_ref refd, refdt;
+
+  while (ptr < end)
+    {
+      op = *ptr++;
+      switch (op)
+       {
+       case DW_OP_addr:
+         ptr += ptr_size;
+         break;
+       case DW_OP_deref:
+       case DW_OP_dup:
+       case DW_OP_drop:
+       case DW_OP_over:
+       case DW_OP_swap:
+       case DW_OP_rot:
+       case DW_OP_xderef:
+       case DW_OP_abs:
+       case DW_OP_and:
+       case DW_OP_div:
+       case DW_OP_minus:
+       case DW_OP_mod:
+       case DW_OP_mul:
+       case DW_OP_neg:
+       case DW_OP_not:
+       case DW_OP_or:
+       case DW_OP_plus:
+       case DW_OP_shl:
+       case DW_OP_shr:
+       case DW_OP_shra:
+       case DW_OP_xor:
+       case DW_OP_eq:
+       case DW_OP_ge:
+       case DW_OP_gt:
+       case DW_OP_le:
+       case DW_OP_lt:
+       case DW_OP_ne:
+       case DW_OP_lit0 ... DW_OP_lit31:
+       case DW_OP_reg0 ... DW_OP_reg31:
+       case DW_OP_nop:
+       case DW_OP_push_object_address:
+       case DW_OP_form_tls_address:
+       case DW_OP_call_frame_cfa:
+       case DW_OP_stack_value:
+       case DW_OP_GNU_push_tls_address:
+       case DW_OP_GNU_uninit:
+         break;
+       case DW_OP_const1u:
+       case DW_OP_pick:
+       case DW_OP_deref_size:
+       case DW_OP_xderef_size:
+       case DW_OP_const1s:
+         ++ptr;
+         break;
+       case DW_OP_const2u:
+       case DW_OP_const2s:
+       case DW_OP_skip:
+       case DW_OP_bra:
+         ptr += 2;
+         break;
+       case DW_OP_call2:
+       case DW_OP_call4:
+       case DW_OP_GNU_parameter_ref:
+         if (op == DW_OP_call2)
+           addr = read_16 (ptr);
+         else
+           addr = read_32 (ptr);
+         refd = off_htab_lookup (ref->die_cu->cu_offset + addr);
+         assert (refd != NULL && !refd->die_remove);
+         if (op == DW_OP_call2)
+           {
+             assert (refd->u.p2.die_new_offset <= 65535);
+             ptr -= 2;
+             write_16 (ptr, refd->u.p2.die_new_offset);
+           }
+         else
+           {
+             ptr -= 4;
+             write_32 (ptr, refd->u.p2.die_new_offset);
+           }
+         break;
+       case DW_OP_const4u:
+       case DW_OP_const4s:
+         ptr += 4;
+         break;
+       case DW_OP_call_ref:
+       case DW_OP_GNU_implicit_pointer:
+         addr = read_size (ptr, ref->die_cu->cu_version == 2 ? ptr_size : 4);
+         assert (die->die_cu->cu_version == ref->die_cu->cu_version);
+         refd = off_htab_lookup (addr);
+         assert (refd != NULL);
+         refdt = refd;
+         while (refdt->die_toplevel == 0)
+           refdt = refdt->die_parent;
+         if (die_dup (refdt) && !refdt->die_op_type_referenced)
+           refd = die_find_dup (refdt, die_dup (refdt), refd);
+         write_size (ptr, die->die_cu->cu_version == 2 ? ptr_size : 4,
+                     refd->die_cu->cu_new_offset
+                     + refd->u.p2.die_new_offset);
+         if (die->die_cu->cu_version == 2)
+           ptr += ptr_size;
+         else
+           ptr += 4;
+         if (op == DW_OP_GNU_implicit_pointer)
+           read_uleb128 (ptr);
+         break;
+       case DW_OP_const8u:
+       case DW_OP_const8s:
+         ptr += 8;
+         break;
+       case DW_OP_constu:
+       case DW_OP_plus_uconst:
+       case DW_OP_regx:
+       case DW_OP_piece:
+       case DW_OP_consts:
+       case DW_OP_breg0 ... DW_OP_breg31:
+       case DW_OP_fbreg:
+         read_uleb128 (ptr);
+         break;
+       case DW_OP_bregx:
+       case DW_OP_bit_piece:
+         read_uleb128 (ptr);
+         read_uleb128 (ptr);
+         break;
+       case DW_OP_implicit_value:
+         leni = read_uleb128 (ptr);
+         ptr += leni;
+         break;
+       case DW_OP_GNU_entry_value:
+         leni = read_uleb128 (ptr);
+         assert ((end - ptr) >= leni);
+         adjust_exprloc (die, ref, ptr, leni);
+         ptr += leni;
+         break;
+       case DW_OP_GNU_convert:
+       case DW_OP_GNU_reinterpret:
+         orig_ptr = ptr;
+         addr = read_uleb128 (ptr);
+         if (addr == 0)
+           break;
+         goto typed_dwarf;
+       case DW_OP_GNU_regval_type:
+         read_uleb128 (ptr);
+         orig_ptr = ptr;
+         addr = read_uleb128 (ptr);
+         goto typed_dwarf;
+       case DW_OP_GNU_const_type:
+         orig_ptr = ptr;
+         addr = read_uleb128 (ptr);
+         goto typed_dwarf;
+       case DW_OP_GNU_deref_type:
+         ++ptr;
+         orig_ptr = ptr;
+         addr = read_uleb128 (ptr);
+       typed_dwarf:
+         refd = off_htab_lookup (ref->die_cu->cu_offset + addr);
+         assert (refd != NULL && refd->die_op_type_referenced);
+         leni = ptr - orig_ptr;
+         assert (size_of_uleb128 (refd->u.p2.die_new_offset) <= leni);
+         ptr = orig_ptr;
+         write_uleb128 (ptr, refd->u.p2.die_new_offset);
+         /* If the new offset ends up being shorter uleb128
+            encoded than the old, pad it up to make it still valid,
+            but not shortest, uleb128.  Changing sizes of
+            exprloc would be a nightmare.  Another alternative would
+            be to pad with DW_OP_nop after the op.  */
+         if (ptr < orig_ptr + leni)
+           {
+             ptr[-1] |= 0x80;
+             while (ptr < orig_ptr + leni - 1)
+               *ptr++ = 0x80;
+             *ptr++ = 0;
+           }
+         if (op == DW_OP_GNU_const_type)
+           ptr += *ptr + 1;
+         break;
+       default:
+         abort ();
+       }
+    }
+}
+
+/* Write DIE (with REF being the corresponding original DIE) to
+   memory starting at PTR, return pointer after the DIE.  */
+static unsigned char *
+write_die (unsigned char *ptr, dw_die_ref die, dw_die_ref ref)
+{
+  dw_die_ref child, sib = NULL;
+  if (die->die_remove)
+    return ptr;
+  if (die->die_offset == -1U)
+    {
+      if (ref != NULL)
+       ;
+      else if (die_safe_nextdup (die) && die_dup (die_nextdup (die)) == die)
+       ref = die_nextdup (die);
+    }
+  else
+    ref = die;
+  if (die->die_child && die->die_sib)
+    for (sib = die->die_sib; sib; sib = sib->die_sib)
+      if (!sib->die_remove)
+       break;
+  write_uleb128 (ptr, die->u.p2.die_new_abbrev->entry);
+  if (ref)
+    {
+      unsigned char *inptr = debug_sections[DEBUG_INFO].data
+                            + ref->die_offset;
+      struct abbrev_tag *reft = ref->die_abbrev;
+      unsigned int i, j;
+
+      read_uleb128 (inptr);
+      for (i = 0, j = 0; i < reft->nattr; ++i)
+       {
+         uint32_t form = reft->attr[i].form;
+         size_t len = 0;
+         uint64_t value;
+         unsigned char *orig_ptr = inptr;
+
+         while (form == DW_FORM_indirect)
+           form = read_uleb128 (inptr);
+
+         switch (form)
+           {
+           case DW_FORM_ref_addr:
+             {
+               dw_die_ref refd, refdt;
+               value = read_size (inptr, ref->die_cu->cu_version == 2
+                                         ? ptr_size : 4);
+               inptr += ref->die_cu->cu_version == 2 ? ptr_size : 4;
+               refd = off_htab_lookup (ref->die_cu->cu_offset + value);
+               assert (refd != NULL);
+               refdt = refd;
+               while (refdt->die_toplevel == 0)
+                 refdt = refdt->die_parent;
+               if (die_dup (refdt) && !refdt->die_op_type_referenced)
+                 refd = die_find_dup (refdt, die_dup (refdt), refd);
+               value = refd->die_cu->cu_new_offset
+                       + refd->u.p2.die_new_offset;
+               write_size (ptr, die->die_cu->cu_version == 2 ? ptr_size : 4,
+                           value);
+               ptr += die->die_cu->cu_version == 2 ? ptr_size : 4;
+               j++;
+               continue;
+             }
+           case DW_FORM_addr:
+             inptr += ptr_size;
+             break;
+           case DW_FORM_flag_present:
+             break;
+           case DW_FORM_flag:
+           case DW_FORM_data1:
+             ++inptr;
+             break;
+           case DW_FORM_data2:
+             inptr += 2;
+             break;
+           case DW_FORM_data4:
+           case DW_FORM_sec_offset:
+             inptr += 4;
+             break;
+           case DW_FORM_data8:
+           case DW_FORM_ref_sig8:
+             inptr += 8;
+             break;
+           case DW_FORM_sdata:
+           case DW_FORM_udata:
+             read_uleb128 (inptr);
+             break;
+           case DW_FORM_strp:
+             inptr += 4;
+             break;
+           case DW_FORM_string:
+             inptr = (unsigned char *) strchr ((char *)inptr, '\0') + 1;
+             break;
+           case DW_FORM_indirect:
+             abort ();
+           case DW_FORM_block1:
+             len = *inptr++;
+             break;
+           case DW_FORM_block2:
+             len = read_16 (inptr);
+             form = DW_FORM_block1;
+             break;
+           case DW_FORM_block4:
+             len = read_32 (inptr);
+             form = DW_FORM_block1;
+             break;
+           case DW_FORM_block:
+             len = read_uleb128 (inptr);
+             form = DW_FORM_block1;
+             break;
+           case DW_FORM_exprloc:
+             len = read_uleb128 (inptr);
+             break;
+           case DW_FORM_ref1:
+           case DW_FORM_ref2:
+           case DW_FORM_ref4:
+           case DW_FORM_ref8:
+           case DW_FORM_ref_udata:
+             switch (form)
+               {
+               case DW_FORM_ref1: value = read_8 (inptr); break;
+               case DW_FORM_ref2: value = read_16 (inptr); break;
+               case DW_FORM_ref4: value = read_32 (inptr); break;
+               case DW_FORM_ref8: value = read_64 (inptr); break;
+               case DW_FORM_ref_udata: value = read_uleb128 (inptr); break;
+               default: abort ();
+               }
+             if (reft->attr[i].attr == DW_AT_sibling)
+               {
+                 if (j == die->u.p2.die_new_abbrev->nattr
+                     || die->u.p2.die_new_abbrev->attr[j].attr
+                        != DW_AT_sibling)
+                   continue;
+                 assert (sib);
+                 value = sib->u.p2.die_new_offset;
+               }
+             else
+               {
+                 dw_die_ref refdt, refd
+                   = off_htab_lookup (ref->die_cu->cu_offset + value);
+                 assert (refd != NULL);
+                 refdt = refd;
+                 while (refdt->die_toplevel == 0)
+                   refdt = refdt->die_parent;
+                 if (die_dup (refdt) && refdt->die_op_type_referenced)
+                   {
+                     if (die->die_cu == die_dup (refdt)->die_cu)
+                       refd = die_find_dup (refdt, die_dup (refdt), refd);
+                   }
+                 else if (die_dup (refdt))
+                   refd = die_find_dup (refdt, die_dup (refdt), refd);
+                 value = refd->u.p2.die_new_offset;
+                 if (die->u.p2.die_new_abbrev->attr[j].form
+                     == DW_FORM_ref_addr)
+                   value += refd->die_cu->cu_new_offset;
+                 else
+                   assert (refd->die_cu == die->die_cu);
+               }
+             switch (die->u.p2.die_new_abbrev->attr[j].form)
+               {
+               case DW_FORM_ref1: write_8 (ptr, value); break;
+               case DW_FORM_ref2: write_16 (ptr, value); break;
+               case DW_FORM_ref4: write_32 (ptr, value); break;
+               case DW_FORM_ref_udata: write_uleb128 (ptr, value); break;
+               case DW_FORM_ref_addr:
+                 write_size (ptr, die->die_cu->cu_version == 2 ? ptr_size : 4,
+                             value);
+                 ptr += die->die_cu->cu_version == 2 ? ptr_size : 4;
+                 break;
+               default:
+                 abort ();
+               }
+             j++;
+             continue;
+           default:
+             abort ();
+           }
+
+         if (form == DW_FORM_block1 || form == DW_FORM_exprloc)
+           inptr += len;
+
+         memcpy (ptr, orig_ptr, inptr - orig_ptr);
+         ptr += inptr - orig_ptr;
+
+         if (form == DW_FORM_block1)
+           switch (reft->attr[i].attr)
+             {
+             case DW_AT_frame_base:
+             case DW_AT_location:
+             case DW_AT_data_member_location:
+             case DW_AT_vtable_elem_location:
+             case DW_AT_byte_size:
+             case DW_AT_bit_offset:
+             case DW_AT_bit_size:
+             case DW_AT_string_length:
+             case DW_AT_lower_bound:
+             case DW_AT_return_addr:
+             case DW_AT_bit_stride:
+             case DW_AT_upper_bound:
+             case DW_AT_count:
+             case DW_AT_segment:
+             case DW_AT_static_link:
+             case DW_AT_use_location:
+             case DW_AT_allocated:
+             case DW_AT_associated:
+             case DW_AT_data_location:
+             case DW_AT_byte_stride:
+             case DW_AT_GNU_call_site_value:
+             case DW_AT_GNU_call_site_data_value:
+             case DW_AT_GNU_call_site_target:
+             case DW_AT_GNU_call_site_target_clobbered:
+               adjust_exprloc (die, ref, ptr - len, len);
+             default:
+               break;
+             }
+         else if (form == DW_FORM_exprloc)
+           adjust_exprloc (die, ref, ptr - len, len);
+         j++;
+       }
+      assert (j == die->u.p2.die_new_abbrev->nattr);
+    }
+  else
+    switch (die->die_tag)
+      {
+      case DW_TAG_partial_unit:
+       if (die->u.p2.die_new_abbrev->nattr == 0)
+         break;
+       if (die->u.p2.die_new_abbrev->attr[0].attr == DW_AT_stmt_list)
+         {
+           enum dwarf_form form;
+           unsigned char *p = get_AT (die_nextdup (die),
+                                      DW_AT_stmt_list, &form);
+           assert (p && (form == DW_FORM_sec_offset
+                         || form == DW_FORM_data4));
+           memcpy (ptr, p, 4);
+           ptr += 4;
+         }
+       if (die->u.p2.die_new_abbrev->attr[die->u.p2.die_new_abbrev->nattr
+                                          - 1].attr
+           == DW_AT_comp_dir)
+         {
+           enum dwarf_form form;
+           unsigned char *p = get_AT (die_nextdup (die),
+                                      DW_AT_comp_dir, &form);
+           assert (p);
+           assert (form
+                   == die->u.p2.die_new_abbrev->attr
+                      [die->u.p2.die_new_abbrev->nattr - 1].form);
+           if (form == DW_FORM_strp)
+             {
+               memcpy (ptr, p, 4);
+               ptr += 4;
+             }
+           else
+             {
+               size_t len = strlen ((char *) p) + 1;
+               memcpy (ptr, p, len);
+               ptr += len;
+             }
+         }
+       break;
+      case DW_TAG_namespace:
+      case DW_TAG_module:
+       {
+         enum dwarf_form form;
+         unsigned char *p = get_AT (die_nextdup (die), DW_AT_name, &form);
+         assert (p && (form == die->u.p2.die_new_abbrev->attr[0].form));
+         if (form == DW_FORM_strp)
+           {
+             memcpy (ptr, p, 4);
+             ptr += 4;
+           }
+         else
+           {
+             size_t len = strlen ((char *) p) + 1;
+             memcpy (ptr, p, len);
+             ptr += len;
+           }
+         if (die->u.p2.die_new_abbrev->nattr > 1)
+           {
+             assert (sib);
+             switch (die->u.p2.die_new_abbrev->attr[1].form)
+               {
+               case DW_FORM_ref1:
+                 write_8 (ptr, sib->u.p2.die_new_offset);
+                 break;
+               case DW_FORM_ref2:
+                 write_16 (ptr, sib->u.p2.die_new_offset);
+                 break;
+               case DW_FORM_ref4:
+                 write_32 (ptr, sib->u.p2.die_new_offset);
+                 break;
+               case DW_FORM_ref_udata:
+                 write_uleb128 (ptr, sib->u.p2.die_new_offset);
+                 break;
+               default:
+                 abort ();
+               }
+           }
+         break;
+       }
+      case DW_TAG_imported_unit:
+       write_size (ptr, die->die_cu->cu_version == 2 ? ptr_size : 4,
+                   die_nextdup (die)->die_cu->cu_new_offset
+                   + die_nextdup (die)->u.p2.die_new_offset);
+       ptr += die->die_cu->cu_version == 2 ? ptr_size : 4;
+       break;
+      default:
+       abort ();
+      }
+  if (ref != NULL && ref != die)
+    {
+      dw_die_ref ref_child;
+      for (child = die->die_child, ref_child = ref->die_child;
+          child; child = child->die_sib, ref_child = ref_child->die_sib)
+       ptr = write_die (ptr, child, ref_child);
+    }
+  else
+    for (child = die->die_child; child; child = child->die_sib)
+      ptr = write_die (ptr, child, NULL);
+  if (die->die_child)
+    write_8 (ptr, 0);
+  return ptr;
+}
+
+/* Construct new .debug_info section of size INFO_SIZE in malloced memory,
+   return pointer to it.  */
+static unsigned char *
+write_info (unsigned long info_size)
+{
+  struct dw_cu *cu;
+  unsigned char *info = xmalloc (info_size);
+  unsigned char *ptr = info;
+
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    {
+      unsigned long next_off = info_size;
+      /* Ignore .debug_types CUs.  */
+      if (cu->cu_die == NULL)
+       break;
+      if (cu->cu_next && cu->cu_next->cu_die)
+       next_off = cu->cu_next->cu_new_offset;
+      /* Write CU header.  */
+      write_32 (ptr, next_off - cu->cu_new_offset - 4);
+      write_16 (ptr, cu->cu_version);
+      write_32 (ptr, cu->u2.cu_new_abbrev_offset);
+      write_8 (ptr, ptr_size);
+      ptr = write_die (ptr, cu->cu_die, NULL);
+      assert (info + next_off == ptr);
+    }
+  assert (info + info_size == ptr);
+  return info;
+}
+
+/* Adjust .debug_loc range determined by *SLOT, called through
+   htab_traverse.  */
+static int
+adjust_loclist (void **slot, void *data __attribute__((unused)))
+{
+  struct debug_loc_adjust *adj = (struct debug_loc_adjust *) *slot;
+  unsigned char *ptr, *endsec;
+  GElf_Addr low, high;
+  size_t len;
+
+  ptr = debug_sections[DEBUG_LOC].data + adj->start_offset;
+  endsec = ptr + debug_sections[DEBUG_LOC].size;
+  while (ptr < endsec)
+    {
+      low = read_size (ptr, ptr_size);
+      high = read_size (ptr + ptr_size, ptr_size);
+      ptr += 2 * ptr_size;
+      if (low == 0 && high == 0)
+       break;
+
+      if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff))
+       continue;
+
+      len = read_16 (ptr);
+      assert (ptr + len <= endsec);
+
+      adjust_exprloc (adj->cu->cu_die, adj->cu->cu_die, ptr, len);
+
+      ptr += len;
+    }
+
+  return 1;
+}
+
+/* Create new .debug_loc section in malloced memory if .debug_loc
+   needs to be adjusted, return it, otherwise return NULL.  */
+static unsigned char *
+write_loc (void)
+{
+  unsigned char *loc, *old;
+  if (loc_htab == NULL)
+    return NULL;
+  loc = xmalloc (debug_sections[DEBUG_LOC].size);
+  old = debug_sections[DEBUG_LOC].data;
+  memcpy (loc, old, debug_sections[DEBUG_LOC].size);
+  debug_sections[DEBUG_LOC].data = loc;
+  htab_traverse (loc_htab, adjust_loclist, NULL);
+  debug_sections[DEBUG_LOC].data = old;
+  return loc;
+}
+
+/* Create new .debug_types section in malloced memory if abbrev
+   offsets in .debug_types need to be adjusted, return it, otherwise
+   return NULL.  */
+static unsigned char *
+write_types (void)
+{
+  struct dw_cu *cu;
+  unsigned char *types, *ptr;
+
+  if (debug_sections[DEBUG_TYPES].data == NULL)
+    return NULL;
+  types = xmalloc (debug_sections[DEBUG_TYPES].size);
+  memcpy (types, debug_sections[DEBUG_TYPES].data,
+         debug_sections[DEBUG_TYPES].size);
+  for (cu = first_cu; cu; cu = cu->cu_next)
+    {
+      /* Ignore .debug_info CUs.  */
+      if (cu->cu_die != NULL)
+       continue;
+      ptr = types + cu->cu_offset + 6;
+      write_32 (ptr, cu->u2.cu_new_abbrev_offset);
+    }
+  return types;
+}
+
+static int
+read_dwarf (DSO *dso)
+{
+  Elf_Data *data;
+  Elf_Scn *scn;
+  int i, j;
+
+  for (i = 0; debug_sections[i].name; ++i)
+    {
+      debug_sections[i].data = NULL;
+      debug_sections[i].size = 0;
+      debug_sections[i].sec = 0;
+    }
+  ptr_size = 0;
+
+  for (i = 1; i < dso->ehdr.e_shnum; ++i)
+    if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR))
+       && dso->shdr[i].sh_size)
+      {
+       const char *name = strptr (dso, dso->ehdr.e_shstrndx,
+                                  dso->shdr[i].sh_name);
+
+       if (strncmp (name, ".debug_", sizeof (".debug_") - 1) == 0)
+         {
+           for (j = 0; debug_sections[j].name; ++j)
+             if (strcmp (name, debug_sections[j].name) == 0)
+               {
+                 if (debug_sections[j].data)
+                   {
+                     error (0, 0, "%s: Found two copies of %s section",
+                            dso->filename, name);
+                     return 1;
+                   }
+
+                 scn = dso->scn[i];
+                 data = elf_rawdata (scn, NULL);
+                 assert (data != NULL && data->d_buf != NULL);
+                 assert (elf_rawdata (scn, data) == NULL);
+                 assert (data->d_off == 0);
+                 assert (data->d_size == dso->shdr[i].sh_size);
+                 debug_sections[j].data = data->d_buf;
+                 debug_sections[j].size = data->d_size;
+                 debug_sections[j].sec = i;
+                 break;
+               }
+
+           if (debug_sections[j].name == NULL)
+             {
+               error (0, 0, "%s: Unknown debugging section %s",
+                      dso->filename, name);
+               return 1;
+             }
+         }
+      }
+
+  if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+    {
+      do_read_16 = buf_read_ule16;
+      do_read_32 = buf_read_ule32;
+      do_read_64 = buf_read_ule64;
+      do_write_16 = buf_write_le16;
+      do_write_32 = buf_write_le32;
+      do_write_64 = buf_write_le64;
+    }
+  else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+    {
+      do_read_16 = buf_read_ube16;
+      do_read_32 = buf_read_ube32;
+      do_read_64 = buf_read_ube64;
+      do_write_16 = buf_write_be16;
+      do_write_32 = buf_write_be32;
+      do_write_64 = buf_write_be64;
+    }
+  else
+    {
+      error (0, 0, "%s: Wrong ELF data enconding", dso->filename);
+      return 1;
+    }
+
+  if (debug_sections[DEBUG_PUBTYPES].data != NULL)
+    {
+      error (0, 0, "%s: .debug_pubtypes adjusting unimplemented",
+            dso->filename);
+      return 1;
+    }
+  if (debug_sections[DEBUG_PUBNAMES].data != NULL)
+    {
+      error (0, 0, "%s: .debug_pubnames adjusting unimplemented",
+            dso->filename);
+      return 1;
+    }
+
+  if (debug_sections[DEBUG_INFO].data != NULL)
+    {
+      if (read_debug_info (dso))
+       return 1;
+    }
+
+  return 0;
+}
+
+static DSO *
+fdopen_dso (int fd, const char *name)
+{
+  Elf *elf = NULL;
+  GElf_Ehdr ehdr;
+  int i;
+  DSO *dso = NULL;
+
+  elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+  if (elf == NULL)
+    {
+      error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
+      goto error_out;
+    }
+
+  if (elf_kind (elf) != ELF_K_ELF)
+    {
+      error (0, 0, "\"%s\" is not an ELF file", name);
+      goto error_out;
+    }
+
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    {
+      error (0, 0, "cannot get the ELF header: %s",
+            elf_errmsg (-1));
+      goto error_out;
+    }
+
+  if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC)
+    {
+      error (0, 0, "\"%s\" is not a shared library", name);
+      goto error_out;
+    }
+
+  /* Allocate DSO structure. Leave place for additional 20 new section
+     headers.  */
+  dso = (DSO *)
+       malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr)
+               + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *));
+  if (!dso)
+    {
+      error (0, ENOMEM, "Could not open DSO");
+      goto error_out;
+    }
+
+  elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE);
+
+  memset (dso, 0, sizeof(DSO));
+  dso->elf = elf;
+  dso->ehdr = ehdr;
+  dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20];
+
+  for (i = 0; i < ehdr.e_shnum; ++i)
+    {
+      dso->scn[i] = elf_getscn (elf, i);
+      gelf_getshdr (dso->scn[i], dso->shdr + i);
+    }
+
+  dso->filename = (const char *) strdup (name);
+  return dso;
+
+error_out:
+  if (dso)
+    {
+      free ((char *) dso->filename);
+      free (dso);
+    }
+  if (elf)
+    elf_end (elf);
+  if (fd != -1)
+    close (fd);
+  return NULL;
+}
+
+/* Store new ELF into FILE.  ABBREV is the new .debug_abbrev
+   section (and ABBREV_SIZE its size), INFO is the new
+   .debug_info section (and INFO_SIZE its size), LOC and TYPES
+   are contents of new .debug_loc resp. debug_types sections if
+   any adjustments are needed.  */
+static void
+write_dso (DSO *dso, const char *file, unsigned char *abbrev,
+          unsigned long abbrev_size, unsigned char *info,
+          unsigned long info_size, unsigned char *loc,
+          unsigned char *types, struct stat *st)
+{
+  Elf *elf = NULL;
+  GElf_Ehdr ehdr;
+  char *e_ident;
+  int fd, i;
+  GElf_Off off, diff;
+
+  diff = (GElf_Off) abbrev_size
+        - (GElf_Off) dso->shdr[debug_sections[DEBUG_ABBREV].sec].sh_size;
+  off = dso->shdr[debug_sections[DEBUG_ABBREV].sec].sh_offset;
+  for (i = 1; i < dso->ehdr.e_shnum; ++i)
+    if (dso->shdr[i].sh_offset > off)
+      dso->shdr[i].sh_offset += diff;
+  if (dso->ehdr.e_shoff > off)
+    dso->ehdr.e_shoff += diff;
+  dso->shdr[debug_sections[DEBUG_ABBREV].sec].sh_size += diff;
+  diff = (GElf_Off) info_size
+        - (GElf_Off) dso->shdr[debug_sections[DEBUG_INFO].sec].sh_size;
+  off = dso->shdr[debug_sections[DEBUG_INFO].sec].sh_offset;
+  for (i = 1; i < dso->ehdr.e_shnum; ++i)
+    if (dso->shdr[i].sh_offset > off)
+      dso->shdr[i].sh_offset += diff;
+  if (dso->ehdr.e_shoff > off)
+    dso->ehdr.e_shoff += diff;
+  dso->shdr[debug_sections[DEBUG_INFO].sec].sh_size += diff;
+
+  fd = open (file, O_RDWR | O_CREAT, 0600);
+  if (fd == -1)
+    error (1, errno, "Failed to open %s for writing", file);
+
+  elf = elf_begin (fd, ELF_C_WRITE_MMAP, NULL);
+  if (elf == NULL)
+    error (1, 0, "cannot open ELF file: %s", elf_errmsg (-1));
+
+  /* Some gelf_newehdr implementations don't return the resulting
+     ElfNN_Ehdr, so we have to do it the hard way instead of:
+     e_ident = (char *) gelf_newehdr (elf, gelf_getclass (dso->elf));  */
+  switch (gelf_getclass (dso->elf))
+    {
+    case ELFCLASS32:
+      e_ident = (char *) elf32_newehdr (elf);
+      break;
+    case ELFCLASS64:
+      e_ident = (char *) elf64_newehdr (elf);
+      break;
+    default:
+      e_ident = NULL;
+      break;
+    }
+
+  if (e_ident == NULL
+      /* This is here just for the gelfx wrapper, so that gelf_update_ehdr
+        already has the correct ELF class.  */
+      || memcpy (e_ident, dso->ehdr.e_ident, EI_NIDENT) == NULL
+      || gelf_update_ehdr (elf, &dso->ehdr) == 0
+      || gelf_newphdr (elf, dso->ehdr.e_phnum) == 0)
+    error (1, 0, "Could not create new ELF headers");
+  ehdr = dso->ehdr;
+  elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE);
+  for (i = 0; i < ehdr.e_phnum; ++i)
+    {
+      GElf_Phdr *phdr, phdr_mem;
+      phdr = gelf_getphdr (dso->elf, i, &phdr_mem);
+      gelf_update_phdr (elf, i, phdr);
+    }
+
+  for (i = 1; i < ehdr.e_shnum; ++i)
+    {
+      Elf_Scn *scn = elf_newscn (elf);
+      Elf_Data *data1, *data2;
+
+      elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+      gelf_update_shdr (scn, &dso->shdr[i]);
+      data1 = elf_getdata (dso->scn[i], NULL);
+      data2 = elf_newdata (scn);
+      memcpy (data2, data1, sizeof (*data1));
+      if (i == debug_sections[DEBUG_ABBREV].sec)
+       {
+         data2->d_buf = abbrev;
+         data2->d_size = dso->shdr[i].sh_size;
+       }
+      else if (i == debug_sections[DEBUG_INFO].sec)
+       {
+         data2->d_buf = info;
+         data2->d_size = dso->shdr[i].sh_size;
+       }
+      else if (loc != NULL && i == debug_sections[DEBUG_LOC].sec)
+       data2->d_buf = loc;
+      else if (types != NULL && i == debug_sections[DEBUG_TYPES].sec)
+       data2->d_buf = types;
+    }
+
+  if (elf_update (elf, ELF_C_WRITE_MMAP) == -1)
+    error (1, 0, "%s: elf_update failed", dso->filename);
+
+  if (elf_end (elf) < 0)
+    error (1, 0, "elf_end failed: %s\n", elf_errmsg (elf_errno()));
+
+  fchown (fd, st->st_uid, st->st_gid);
+  fchmod (fd, st->st_mode & 07777);
+  close (fd);
+}
+
+int
+main (int argc, char *argv[])
+{
+  DSO *dso;
+  int fd;
+  const char *file, *outfile;
+  unsigned long new_debug_info_size, new_debug_abbrev_size;
+  unsigned char *new_debug_abbrev, *new_debug_info;
+  unsigned char *new_debug_loc, *new_debug_types;
+  struct stat st;
+
+  if (argc < 3)
+    error (1, 0, "Usage: dwz input_object output_object");
+
+  file = argv[1];
+  outfile = argv[2];
+
+  if (elf_version (EV_CURRENT) == EV_NONE)
+    error (1, 0, "library out of date\n");
+
+  fd = open (file, O_RDONLY);
+  if (fd < 0)
+    error (1, errno, "Failed to open input file %s", file);
+  if (fstat (fd, &st) < 0)
+    error (1, errno, "Failed to stat input file %s", file);
+
+  dso = fdopen_dso (fd, file);
+  if (dso == NULL)
+    exit (1);
+
+  if (read_dwarf (dso))
+    exit (1);
+
+  if (partition_dups ())
+    exit (1);
+
+  if (create_import_tree ())
+    exit (1);
+
+  if (read_debug_types (dso))
+    exit (1);
+
+  if (compute_abbrevs (dso, &new_debug_info_size, &new_debug_abbrev_size))
+    exit (1);
+
+  if (new_debug_info_size + new_debug_abbrev_size
+      >= debug_sections[DEBUG_INFO].size + debug_sections[DEBUG_ABBREV].size)
+    error (1, 0, "%s: DWARF compression not beneficial "
+                "- old size %ld new size %ld", dso->filename,
+          (unsigned long) (debug_sections[DEBUG_INFO].size
+                           + debug_sections[DEBUG_ABBREV].size),
+          new_debug_info_size + new_debug_abbrev_size);
+
+  new_debug_abbrev = write_abbrev (new_debug_abbrev_size);
+  new_debug_info = write_info (new_debug_info_size);
+  new_debug_loc = write_loc ();
+  new_debug_types = write_types ();
+
+  write_dso (dso, outfile, new_debug_abbrev, new_debug_abbrev_size,
+            new_debug_info, new_debug_info_size, new_debug_loc,
+            new_debug_types, &st);
+
+  if (elf_end (dso->elf) < 0)
+    error (1, 0, "elf_end failed: %s\n", elf_errmsg (elf_errno()));
+  close (fd);
+
+  return 0;
+}
diff --git a/hashtab.c b/hashtab.c
new file mode 100644 (file)
index 0000000..8a14c09
--- /dev/null
+++ b/hashtab.c
@@ -0,0 +1,750 @@
+/* An expandable hash tables datatype.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2009, 2010
+   Free Software Foundation, Inc.
+   Contributed by Vladimir Makarov (vmakarov@cygnus.com).
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* This package implements basic hash table functionality.  It is possible
+   to search for an entry, create an entry and destroy an entry.
+
+   Elements in the table are generic pointers.
+
+   The size of the table is not fixed; if the occupancy of the table
+   grows too high the hash table will be expanded.
+
+   The abstract data implementation is based on generalized Algorithm D
+   from Knuth's book "The art of computer programming".  Hash table is
+   expanded by creation of new hash table and transferring elements from
+   the old table to the new table. */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "hashtab.h"
+
+/* This macro defines reserved value for empty table entry. */
+
+#define EMPTY_ENTRY    ((void *) 0)
+
+/* This macro defines reserved value for table entry which contained
+   a deleted element. */
+
+#define DELETED_ENTRY  ((void *) 1)
+
+static unsigned long higher_prime_number (unsigned long);
+static hashval_t hash_pointer (const void *);
+static int eq_pointer (const void *, const void *);
+static int htab_expand (htab_t);
+static void **find_empty_slot_for_expand  (htab_t, hashval_t);
+
+/* At some point, we could make these be NULL, and modify the
+   hash-table routines to handle NULL specially; that would avoid
+   function-call overhead for the common case of hashing pointers.  */
+htab_hash htab_hash_pointer = hash_pointer;
+htab_eq htab_eq_pointer = eq_pointer;
+
+/* The following function returns a nearest prime number which is
+   greater than N, and near a power of two. */
+
+static unsigned long
+higher_prime_number (n)
+     unsigned long n;
+{
+  /* These are primes that are near, but slightly smaller than, a
+     power of two.  */
+  static unsigned long primes[] = {
+    (unsigned long) 2,
+    (unsigned long) 7,
+    (unsigned long) 13,
+    (unsigned long) 31,
+    (unsigned long) 61,
+    (unsigned long) 127,
+    (unsigned long) 251,
+    (unsigned long) 509,
+    (unsigned long) 1021,
+    (unsigned long) 2039,
+    (unsigned long) 4093,
+    (unsigned long) 8191,
+    (unsigned long) 16381,
+    (unsigned long) 32749,
+    (unsigned long) 65521,
+    (unsigned long) 131071,
+    (unsigned long) 262139,
+    (unsigned long) 524287,
+    (unsigned long) 1048573,
+    (unsigned long) 2097143,
+    (unsigned long) 4194301,
+    (unsigned long) 8388593,
+    (unsigned long) 16777213,
+    (unsigned long) 33554393,
+    (unsigned long) 67108859,
+    (unsigned long) 134217689,
+    (unsigned long) 268435399,
+    (unsigned long) 536870909,
+    (unsigned long) 1073741789,
+    (unsigned long) 2147483647,
+                                       /* 4294967291L */
+    ((unsigned long) 2147483647) + ((unsigned long) 2147483644),
+  };
+
+  unsigned long* low = &primes[0];
+  unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])];
+
+  while (low != high)
+    {
+      unsigned long* mid = low + (high - low) / 2;
+      if (n > *mid)
+       low = mid + 1;
+      else
+       high = mid;
+    }
+
+  /* If we've run out of primes, abort.  */
+  if (n > *low)
+    {
+      fprintf (stderr, "Cannot find prime bigger than %lu\n", n);
+      abort ();
+    }
+
+  return *low;
+}
+
+/* Returns a hash code for P.  */
+
+static hashval_t
+hash_pointer (p)
+     const void * p;
+{
+  return (hashval_t) ((long)p >> 3);
+}
+
+/* Returns non-zero if P1 and P2 are equal.  */
+
+static int
+eq_pointer (p1, p2)
+     const void * p1;
+     const void * p2;
+{
+  return p1 == p2;
+}
+
+/* This function creates table with length slightly longer than given
+   source length.  The created hash table is initiated as empty (all the
+   hash table entries are EMPTY_ENTRY).  The function returns the created
+   hash table.  Memory allocation may fail; it may return NULL.  */
+
+htab_t
+htab_try_create (size, hash_f, eq_f, del_f)
+     size_t size;
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
+{
+  htab_t result;
+
+  size = higher_prime_number (size);
+  result = (htab_t) calloc (1, sizeof (struct htab));
+  if (result == NULL)
+    return NULL;
+
+  result->entries = (void **) calloc (size, sizeof (void *));
+  if (result->entries == NULL)
+    {
+      free (result);
+      return NULL;
+    }
+
+  result->size = size;
+  result->hash_f = hash_f;
+  result->eq_f = eq_f;
+  result->del_f = del_f;
+  result->return_allocation_failure = 1;
+  return result;
+}
+
+/* This function frees all memory allocated for given hash table.
+   Naturally the hash table must already exist. */
+
+void
+htab_delete (htab)
+     htab_t htab;
+{
+  int i;
+
+  if (htab->del_f)
+    for (i = htab->size - 1; i >= 0; i--)
+      if (htab->entries[i] != EMPTY_ENTRY
+         && htab->entries[i] != DELETED_ENTRY)
+       (*htab->del_f) (htab->entries[i]);
+
+  free (htab->entries);
+  free (htab);
+}
+
+/* This function clears all entries in the given hash table.  */
+
+void
+htab_empty (htab)
+     htab_t htab;
+{
+  int i;
+
+  if (htab->del_f)
+    for (i = htab->size - 1; i >= 0; i--)
+      if (htab->entries[i] != EMPTY_ENTRY
+         && htab->entries[i] != DELETED_ENTRY)
+       (*htab->del_f) (htab->entries[i]);
+
+  memset (htab->entries, 0, htab->size * sizeof (void *));
+  htab->n_deleted = 0;
+  htab->n_elements = 0;
+}
+
+/* Similar to htab_find_slot, but without several unwanted side effects:
+    - Does not call htab->eq_f when it finds an existing entry.
+    - Does not change the count of elements/searches/collisions in the
+      hash table.
+   This function also assumes there are no deleted entries in the table.
+   HASH is the hash value for the element to be inserted.  */
+
+static void **
+find_empty_slot_for_expand (htab, hash)
+     htab_t htab;
+     hashval_t hash;
+{
+  size_t size = htab->size;
+  hashval_t hash2 = 1 + hash % (size - 2);
+  unsigned int index = hash % size;
+
+  for (;;)
+    {
+      void **slot = htab->entries + index;
+
+      if (*slot == EMPTY_ENTRY)
+       return slot;
+      else if (*slot == DELETED_ENTRY)
+       abort ();
+
+      index += hash2;
+      if (index >= size)
+       index -= size;
+    }
+}
+
+/* The following function changes size of memory allocated for the
+   entries and repeatedly inserts the table elements.  The occupancy
+   of the table after the call will be about 50%.  Naturally the hash
+   table must already exist.  Remember also that the place of the
+   table entries is changed.  If memory allocation failures are allowed,
+   this function will return zero, indicating that the table could not be
+   expanded.  If all goes well, it will return a non-zero value.  */
+
+static int
+htab_expand (htab)
+     htab_t htab;
+{
+  void **oentries;
+  void **olimit;
+  void **p;
+
+  oentries = htab->entries;
+  olimit = oentries + htab->size;
+
+  htab->size = higher_prime_number (htab->size * 2);
+
+  if (htab->return_allocation_failure)
+    {
+      void **nentries = (void **) calloc (htab->size, sizeof (void **));
+      if (nentries == NULL)
+       return 0;
+      htab->entries = nentries;
+    }
+
+  htab->n_elements -= htab->n_deleted;
+  htab->n_deleted = 0;
+
+  p = oentries;
+  do
+    {
+      void * x = *p;
+
+      if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
+       {
+         void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x));
+
+         *q = x;
+       }
+
+      p++;
+    }
+  while (p < olimit);
+
+  free (oentries);
+  return 1;
+}
+
+/* This function searches for a hash table entry equal to the given
+   element.  It cannot be used to insert or delete an element.  */
+
+void *
+htab_find_with_hash (htab, element, hash)
+     htab_t htab;
+     const void * element;
+     hashval_t hash;
+{
+  unsigned int index;
+  hashval_t hash2;
+  size_t size;
+  void * entry;
+
+  htab->searches++;
+  size = htab->size;
+  index = hash % size;
+
+  entry = htab->entries[index];
+  if (entry == EMPTY_ENTRY
+      || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+    return entry;
+
+  hash2 = 1 + hash % (size - 2);
+
+  for (;;)
+    {
+      htab->collisions++;
+      index += hash2;
+      if (index >= size)
+       index -= size;
+
+      entry = htab->entries[index];
+      if (entry == EMPTY_ENTRY
+         || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+       return entry;
+    }
+}
+
+/* Like htab_find_slot_with_hash, but compute the hash value from the
+   element.  */
+
+void *
+htab_find (htab, element)
+     htab_t htab;
+     const void * element;
+{
+  return htab_find_with_hash (htab, element, (*htab->hash_f) (element));
+}
+
+/* This function searches for a hash table slot containing an entry
+   equal to the given element.  To delete an entry, call this with
+   INSERT = 0, then call htab_clear_slot on the slot returned (possibly
+   after doing some checks).  To insert an entry, call this with
+   INSERT = 1, then write the value you want into the returned slot.
+   When inserting an entry, NULL may be returned if memory allocation
+   fails.  */
+
+void **
+htab_find_slot_with_hash (htab, element, hash, insert)
+     htab_t htab;
+     const void * element;
+     hashval_t hash;
+     enum insert_option insert;
+{
+  void **first_deleted_slot;
+  unsigned int index;
+  hashval_t hash2;
+  size_t size;
+
+  if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4
+      && htab_expand (htab) == 0)
+    return NULL;
+
+  size = htab->size;
+  hash2 = 1 + hash % (size - 2);
+  index = hash % size;
+
+  htab->searches++;
+  first_deleted_slot = NULL;
+
+  for (;;)
+    {
+      void * entry = htab->entries[index];
+      if (entry == EMPTY_ENTRY)
+       {
+         if (insert == NO_INSERT)
+           return NULL;
+
+         htab->n_elements++;
+
+         if (first_deleted_slot)
+           {
+             *first_deleted_slot = EMPTY_ENTRY;
+             return first_deleted_slot;
+           }
+
+         return &htab->entries[index];
+       }
+
+      if (entry == DELETED_ENTRY)
+       {
+         if (!first_deleted_slot)
+           first_deleted_slot = &htab->entries[index];
+       }
+      else  if ((*htab->eq_f) (entry, element))
+       return &htab->entries[index];
+
+      htab->collisions++;
+      index += hash2;
+      if (index >= size)
+       index -= size;
+    }
+}
+
+/* Like htab_find_slot_with_hash, but compute the hash value from the
+   element.  */
+
+void **
+htab_find_slot (htab, element, insert)
+     htab_t htab;
+     const void * element;
+     enum insert_option insert;
+{
+  return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element),
+                                  insert);
+}
+
+/* This function deletes an element with the given value from hash
+   table.  If there is no matching element in the hash table, this
+   function does nothing.  */
+
+void
+htab_remove_elt (htab, element)
+     htab_t htab;
+     void * element;
+{
+  void **slot;
+
+  slot = htab_find_slot (htab, element, NO_INSERT);
+  if (*slot == EMPTY_ENTRY)
+    return;
+
+  if (htab->del_f)
+    (*htab->del_f) (*slot);
+
+  *slot = DELETED_ENTRY;
+  htab->n_deleted++;
+}
+
+/* This function clears a specified slot in a hash table.  It is
+   useful when you've already done the lookup and don't want to do it
+   again.  */
+
+void
+htab_clear_slot (htab, slot)
+     htab_t htab;
+     void **slot;
+{
+  if (slot < htab->entries || slot >= htab->entries + htab->size
+      || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY)
+    abort ();
+
+  if (htab->del_f)
+    (*htab->del_f) (*slot);
+
+  *slot = DELETED_ENTRY;
+  htab->n_deleted++;
+}
+
+/* This function scans over the entire hash table calling
+   CALLBACK for each live entry.  If CALLBACK returns false,
+   the iteration stops.  INFO is passed as CALLBACK's second
+   argument.  */
+
+void
+htab_traverse (htab, callback, info)
+     htab_t htab;
+     htab_trav callback;
+     void * info;
+{
+  void **slot = htab->entries;
+  void **limit = slot + htab->size;
+
+  do
+    {
+      void * x = *slot;
+
+      if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
+       if (!(*callback) (slot, info))
+         break;
+    }
+  while (++slot < limit);
+}
+
+/* Return the current size of given hash table. */
+
+size_t
+htab_size (htab)
+     htab_t htab;
+{
+  return htab->size;
+}
+
+/* Return the current number of elements in given hash table. */
+
+size_t
+htab_elements (htab)
+     htab_t htab;
+{
+  return htab->n_elements - htab->n_deleted;
+}
+
+/* Return the fraction of fixed collisions during all work with given
+   hash table. */
+
+double
+htab_collisions (htab)
+     htab_t htab;
+{
+  if (htab->searches == 0)
+    return 0.0;
+
+  return (double) htab->collisions / (double) htab->searches;
+}
+
+#ifndef NDEBUG
+void
+htab_dump (htab, name, dumpfn)
+     htab_t htab;
+     const char *name;
+     htab_dumpfn dumpfn;
+{
+  FILE *f = fopen (name, "w");
+  size_t i, j;
+
+  if (f == NULL)
+    abort ();
+  fprintf (f, "size %zd n_elements %zd n_deleted %zd\n",
+          htab->size, htab->n_elements, htab->n_deleted);
+  for (i = 0; i < htab->size; ++i)
+    {
+      if (htab->entries [i] == EMPTY_ENTRY
+         || htab->entries [i] == DELETED_ENTRY)
+       {
+         for (j = i + 1; j < htab->size; ++j)
+           if (htab->entries [j] != htab->entries [i])
+             break;
+         fprintf (f, "%c%zd\n",
+                  htab->entries [i] == EMPTY_ENTRY ? 'E' : 'D',
+                  j - i);
+         i = j - 1;
+       }
+      else
+       {
+         fputc ('V', f);
+         (*dumpfn) (f, htab->entries [i]);
+       }
+    }
+  fclose (f);
+}
+
+void
+htab_restore (htab, name, restorefn)
+     htab_t htab;
+     const char *name;
+     htab_restorefn restorefn;
+{
+  FILE *f = fopen (name, "r");
+  size_t size, n_elements, n_deleted, i, j, k;
+  int c;
+
+  if (f == NULL)
+    abort ();
+  if (fscanf (f, "size %zd n_elements %zd n_deleted %zd\n",
+             &size, &n_elements, &n_deleted) != 3)
+    abort ();
+  htab_empty (htab);
+  free (htab->entries);
+  htab->entries = (void **) calloc (size, sizeof (void *));
+  if (htab->entries == NULL)
+    abort ();
+  htab->size = size;
+  htab->n_elements = n_elements;
+  htab->n_deleted = n_deleted;
+  for (i = 0; i < htab->size; ++i)
+    {
+      switch ((c = fgetc (f)))
+       {
+       case 'E':
+       case 'D':
+         if (fscanf (f, "%zd\n", &j) != 1)
+           abort ();
+         if (i + j > htab->size)
+           abort ();
+         if (c == 'D')
+           for (k = i; k < i + j; ++k)
+             htab->entries [k] = DELETED_ENTRY;
+         i += j - 1;
+         break;
+       case 'V':
+         htab->entries [i] = (*restorefn) (f);
+         break;
+       default:
+         abort ();
+       }
+    }
+  fclose (f);
+}
+#endif
+
+/* DERIVED FROM:
+--------------------------------------------------------------------
+lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+hash(), hash2(), hash3, and mix() are externally useful functions.
+Routines to test the hash are included if SELF_TEST is defined.
+You can use this free for any purpose.  It has no warranty.
+--------------------------------------------------------------------
+*/
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+  high bits or all three low bits, whether the original value of a,b,c
+  is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+  have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+  2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a 
+  structure that could supported 2x parallelism, like so:
+      a -= b; 
+      a -= c; x = (c>>13);
+      b -= c; a ^= x;
+      b -= a; x = (a<<8);
+      c -= a; b ^= x;
+      c -= b; x = (b>>13);
+      ...
+  Unfortunately, superscalar Pentiums and Sparcs can't take advantage 
+  of that parallelism.  They've also turned some of those single-cycle
+  latency instructions into multi-cycle latency instructions.  Still,
+  this is the fastest good hash I could find.  There were about 2^^68
+  to choose from.  I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+/* same, but slower, works on systems that might have 8 byte hashval_t's */
+#define mix(a,b,c) \
+{ \
+  a -= b; a -= c; a ^= (c>>13); \
+  b -= c; b -= a; b ^= (a<< 8); \
+  c -= a; c -= b; c ^= ((b&0xffffffff)>>13); \
+  a -= b; a -= c; a ^= ((c&0xffffffff)>>12); \
+  b -= c; b -= a; b = (b ^ (a<<16)) & 0xffffffff; \
+  c -= a; c -= b; c = (c ^ (b>> 5)) & 0xffffffff; \
+  a -= b; a -= c; a = (a ^ (c>> 3)) & 0xffffffff; \
+  b -= c; b -= a; b = (b ^ (a<<10)) & 0xffffffff; \
+  c -= a; c -= b; c = (c ^ (b>>15)) & 0xffffffff; \
+}
+
+/*
+--------------------------------------------------------------------
+hash() -- hash a variable-length key into a 32-bit value
+  k     : the key (the unaligned variable-length array of bytes)
+  len   : the length of the key, counting by bytes
+  level : can be any 4-byte value
+Returns a 32-bit value.  Every bit of the key affects every bit of
+the return value.  Every 1-bit and 2-bit delta achieves avalanche.
+About 36+6len instructions.
+
+The best hash table sizes are powers of 2.  There is no need to do
+mod a prime (mod is sooo slow!).  If you need less than 32 bits,
+use a bitmask.  For example, if you need only 10 bits, do
+  h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+  for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
+code any way you wish, private, educational, or commercial.  It's free.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^32 is
+acceptable.  Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+hashval_t
+iterative_hash (const void *k_in /* the key */,
+                register size_t  length /* the length of the key */,
+                register hashval_t initval /* the previous hash, or
+                                              an arbitrary value */)
+{
+  register const unsigned char *k = (const unsigned char *)k_in;
+  register hashval_t a,b,c,len;
+
+  /* Set up the internal state */
+  len = length;
+  a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
+  c = initval;           /* the previous hash value */
+
+  /*---------------------------------------- handle most of the key */
+#ifndef WORDS_BIGENDIAN
+  /* On a little-endian machine, if the data is 4-byte aligned we can hash
+     by word for better speed.  This gives nondeterministic results on
+     big-endian machines.  */
+  if (sizeof (hashval_t) == 4 && (((size_t)k)&3) == 0)
+    while (len >= 12)    /* aligned */
+      {
+       a += *(hashval_t *)(k+0);
+       b += *(hashval_t *)(k+4);
+       c += *(hashval_t *)(k+8);
+       mix(a,b,c);
+       k += 12; len -= 12;
+      }
+  else /* unaligned */
+#endif
+    while (len >= 12)
+      {
+       a += (k[0] +((hashval_t)k[1]<<8) +((hashval_t)k[2]<<16) +((hashval_t)k[3]<<24));
+       b += (k[4] +((hashval_t)k[5]<<8) +((hashval_t)k[6]<<16) +((hashval_t)k[7]<<24));
+       c += (k[8] +((hashval_t)k[9]<<8) +((hashval_t)k[10]<<16)+((hashval_t)k[11]<<24));
+       mix(a,b,c);
+       k += 12; len -= 12;
+      }
+
+  /*------------------------------------- handle the last 11 bytes */
+  c += length;
+  switch(len)              /* all the case statements fall through */
+    {
+    case 11: c+=((hashval_t)k[10]<<24);
+    case 10: c+=((hashval_t)k[9]<<16);
+    case 9 : c+=((hashval_t)k[8]<<8);
+      /* the first byte of c is reserved for the length */
+    case 8 : b+=((hashval_t)k[7]<<24);
+    case 7 : b+=((hashval_t)k[6]<<16);
+    case 6 : b+=((hashval_t)k[5]<<8);
+    case 5 : b+=k[4];
+    case 4 : a+=((hashval_t)k[3]<<24);
+    case 3 : a+=((hashval_t)k[2]<<16);
+    case 2 : a+=((hashval_t)k[1]<<8);
+    case 1 : a+=k[0];
+      /* case 0: nothing left to add */
+    }
+  mix(a,b,c);
+  /*-------------------------------------------- report the result */
+  return c;
+}
diff --git a/hashtab.h b/hashtab.h
new file mode 100644 (file)
index 0000000..b0056d3
--- /dev/null
+++ b/hashtab.h
@@ -0,0 +1,161 @@
+/* An expandable hash tables datatype.
+   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2009, 2010
+   Free Software Foundation, Inc.
+   Contributed by Vladimir Makarov (vmakarov@cygnus.com).
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* This package implements basic hash table functionality.  It is possible
+   to search for an entry, create an entry and destroy an entry.
+
+   Elements in the table are generic pointers.
+
+   The size of the table is not fixed; if the occupancy of the table
+   grows too high the hash table will be expanded.
+
+   The abstract data implementation is based on generalized Algorithm D
+   from Knuth's book "The art of computer programming".  Hash table is
+   expanded by creation of new hash table and transferring elements from
+   the old table to the new table.  */
+
+#ifndef __HASHTAB_H__
+#define __HASHTAB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* The type for a hash code.  */
+typedef unsigned int hashval_t;
+
+/* Callback function pointer types.  */
+
+/* Calculate hash of a table entry.  */
+typedef hashval_t (*htab_hash) (const void *);
+
+/* Compare a table entry with a possible entry.  The entry already in
+   the table always comes first, so the second element can be of a
+   different type (but in this case htab_find and htab_find_slot
+   cannot be used; instead the variants that accept a hash value
+   must be used).  */
+typedef int (*htab_eq) (const void *, const void *);
+
+/* Cleanup function called whenever a live element is removed from
+   the hash table.  */
+typedef void (*htab_del) (void *);
+
+/* Function called by htab_traverse for each live element.  The first
+   arg is the slot of the element (which can be passed to htab_clear_slot
+   if desired), the second arg is the auxiliary pointer handed to
+   htab_traverse.  Return 1 to continue scan, 0 to stop.  */
+typedef int (*htab_trav) (void **, void *);
+
+/* Hash tables are of the following type.  The structure
+   (implementation) of this type is not needed for using the hash
+   tables.  All work with hash table should be executed only through
+   functions mentioned below. */
+
+struct htab
+{
+  /* Pointer to hash function.  */
+  htab_hash hash_f;
+
+  /* Pointer to comparison function.  */
+  htab_eq eq_f;
+
+  /* Pointer to cleanup function.  */
+  htab_del del_f;
+
+  /* Table itself.  */
+  void **entries;
+
+  /* Current size (in entries) of the hash table */
+  size_t size;
+
+  /* Current number of elements including also deleted elements */
+  size_t n_elements;
+
+  /* Current number of deleted elements in the table */
+  size_t n_deleted;
+
+  /* The following member is used for debugging. Its value is number
+     of all calls of `htab_find_slot' for the hash table. */
+  unsigned int searches;
+
+  /* The following member is used for debugging.  Its value is number
+     of collisions fixed for time of work with the hash table. */
+  unsigned int collisions;
+
+  /* This is non-zero if we are allowed to return NULL for function calls
+     that allocate memory.  */
+  int return_allocation_failure;
+};
+
+typedef struct htab *htab_t;
+
+/* An enum saying whether we insert into the hash table or not.  */
+enum insert_option {NO_INSERT, INSERT};
+
+/* The prototypes of the package functions. */
+
+/* This function is like htab_create, but may return NULL if memory
+   allocation fails, and also signals that htab_find_slot_with_hash and
+   htab_find_slot are allowed to return NULL when inserting.  */
+extern htab_t  htab_try_create (size_t, htab_hash, htab_eq, htab_del);
+extern void    htab_delete     (htab_t);
+extern void    htab_empty      (htab_t);
+
+extern void    *htab_find      (htab_t, const void *);
+extern void    **htab_find_slot        (htab_t, const void *, enum insert_option);
+extern void    *htab_find_with_hash (htab_t, const void *, hashval_t);
+extern void    **htab_find_slot_with_hash (htab_t, const void *, hashval_t,
+                                         enum insert_option);
+extern void    htab_clear_slot (htab_t, void **);
+extern void    htab_remove_elt (htab_t, void *);
+
+extern void    htab_traverse   (htab_t, htab_trav, void *);
+
+extern size_t  htab_size       (htab_t);
+extern size_t  htab_elements   (htab_t);
+extern double  htab_collisions (htab_t);
+
+/* A hash function for pointers.  */
+extern htab_hash htab_hash_pointer;
+
+/* An equality function for pointers.  */
+extern htab_eq htab_eq_pointer;
+
+#ifndef NDEBUG
+
+#include <stdio.h>
+
+typedef void (*htab_dumpfn) (FILE *, const void *);
+typedef void *(*htab_restorefn) (FILE *);
+
+extern void    htab_dump (htab_t, const char *, htab_dumpfn);
+extern void    htab_restore (htab_t, const char *, htab_restorefn);
+
+#endif
+
+/* An iterative hash function for arbitrary data.  */
+extern hashval_t iterative_hash (const void *, size_t, hashval_t);
+/* Shorthand for hashing something with an intrinsic size.  */
+#define iterative_hash_object(OB,INIT) iterative_hash (&OB, sizeof (OB), INIT)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __HASHTAB_H */
This page took 0.183828 seconds and 5 git commands to generate.