summaryrefslogtreecommitdiffstats
path: root/dwz.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:55:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:55:52 +0000
commitf7a951d79bc895eb2171c2570add9f4899794a10 (patch)
treecc0c7147f472fecbc93add134f5c0e5c1bb72529 /dwz.c
parentInitial commit. (diff)
downloaddwz-f7a951d79bc895eb2171c2570add9f4899794a10.tar.xz
dwz-f7a951d79bc895eb2171c2570add9f4899794a10.zip
Adding upstream version 0.15.upstream/0.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--dwz.c16872
1 files changed, 16872 insertions, 0 deletions
diff --git a/dwz.c b/dwz.c
new file mode 100644
index 0000000..3bc6038
--- /dev/null
+++ b/dwz.c
@@ -0,0 +1,16872 @@
+/* Copyright (C) 2001-2021 Red Hat, Inc.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Copyright (C) 2019-2021 SUSE LLC.
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/wait.h>
+
+#include <obstack.h>
+
+#include <gelf.h>
+#include <xxhash.h>
+
+#include "dwarf2.h"
+#include "hashtab.h"
+#include "sha1.h"
+#include "args.h"
+#include "util.h"
+#include "pool.h"
+
+#ifndef SHF_COMPRESSED
+ /* Glibc elf.h contains SHF_COMPRESSED starting v2.22. Libelf libelf.h has
+ a fallback definition starting v0.166. Define a fallback definition here
+ for the case of both pre-v2.22 glibc and pre-v0.166 libelf. */
+# define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
+#endif
+
+/* Theory of operation:
+ The DWZ tool can either optimize debug sections of a single
+ executable or shared library at a time, or, when -m option
+ is used, optimize debug sections even in between different
+ executables or shared libraries by constructing a new ET_REL
+ ELF object containing debug sections to which the individual
+ debug sections in the various executables or shared libraries
+ can refer. As debug info can be pretty large, the multifile
+ optimization is done in several phases in order to decrease
+ memory usage of the tool, which can still be quite high.
+
+ The dwz () function optimizes a single file, and at the end,
+ after processing that single file, it frees all allocated memory.
+ Without -m, the dwz () function is called once on each argument.
+
+ When -m has been passed, the dwz () function is first called on
+ each argument, and during it in addition to performing the same
+ optimizations as dwz tool does without -m it also may append
+ data to several temporary files (one for each .debug_* section
+ that is needed for the multifile optimization). During
+ preparation of the additions to the temporary files (write_multifile
+ function), wr_multifile flag is true.
+
+ Next phase (optimize_multifile) is that all these temporary files
+ are mmapped, it is determined what DIEs, strings, .debug_macro
+ sequences etc. might be beneficial to have in the common debug
+ sections and finally a new ET_REL ELF object is written. During
+ this phase the op_multifile flag is true. This is often very
+ memory consuming phase, so extra hacks are used to decrease
+ the memory usage during it.
+
+ Next phase (read_multifile) is where the previously written ET_REL
+ ELF object is parsed again and needed hash tables and other data
+ structures filled in. The rd_multifile flag is set during this
+ phase. The reason why this phase is separate from the previous one,
+ as opposed to just populating the hash tables right away in
+ optimize_multifile, is that the memory consumption during that phase
+ can be very big and keeping malloced data around would mean the address
+ space would be unnecessarily fragmented. read_multifile usually needs
+ to allocate only small fragment of the memory optimize_multifile
+ needs, on the other side that memory needs to be kept around until
+ the end of the last phase.
+
+ During the last phase, the dwz () function is called second
+ time on each argument, with fi_multifile flag set. During this
+ phase duplicates in the common debug sections are referenced
+ from the local debug sections where possible.
+
+ If some executable or shared library has too large debug information
+ (number of DIEs in .debug_info section) that there would be
+ risk of too high memory consumption, that file isn't multifile
+ optimized, instead it is processed by dwz () in a low-memory mode
+ with low_mem flag set. This can decrease memory consumption to
+ half in some very large cases. */
+
+#ifndef NT_GNU_BUILD_ID
+# define NT_GNU_BUILD_ID 3
+#endif
+
+/* xxHash state object. Init in main. */
+static XXH64_state_t *state;
+
+/* Clear xxHash state to zero. */
+#define hash_init_state() XXH64_reset(state, 0)
+
+/* Update hash STATE with VALUE. */
+#define hash_update_state_object(value) XXH64_update(state, &value, sizeof value)
+
+/* Update hash STATE with OBJECT that has a provided SIZE. */
+#define hash_update_state(object, size) XXH64_update(state, object, size)
+
+/* Get digest once we are done with a state. */
+#define hash_digest() XXH64_digest(state)
+
+/* Shorthand for hashing something with an intrinsic size. */
+#define hash(IN,LEN) XXH64(IN, LEN, 0)
+#define iterative_hash(IN,LEN,INIT) XXH64(IN, LEN, INIT)
+#define iterative_hash_object(OB,INIT) iterative_hash (&OB, sizeof (OB), INIT)
+
+/* Print memory amount M (in kb) in both exact and human readable, like so:
+ 1382508 (1.3G). */
+static void
+print_mem (long m)
+{
+ float h = m;
+ int level = 0;
+ const char *unit[] = { "K", "M", "G"};
+ while (h > 1024 && level <= 2)
+ {
+ h = h / 1024;
+ level++;
+ }
+ fprintf (stderr, "%ld (%.1f%s)\n", m, h, unit[level]);
+}
+
+static void
+report_progress (void)
+{
+ static struct tms current;
+ static struct tms prev;
+ static bool first = true;
+ static long ticks_per_second = 0;
+
+ if (!first)
+ prev = current;
+
+ times (&current);
+
+ if (first)
+ {
+ ticks_per_second = sysconf (_SC_CLK_TCK);
+ first = false;
+ return;
+ }
+
+ clock_t user = current.tms_utime - prev.tms_utime;
+ clock_t sys = current.tms_stime - prev.tms_stime;
+ fprintf (stderr, "user: %.2f\n", (float)user / (float)ticks_per_second);
+ fprintf (stderr, "sys : %.2f\n", (float)sys / (float)ticks_per_second);
+
+ if (progress_mem_p)
+ {
+ FILE *s = fopen ("/proc/self/status", "r");
+ char *p;
+ bool print_next = false;
+ for (p = NULL; fscanf (s, "%ms", &p) && p != NULL; free (p))
+ {
+ if (print_next)
+ {
+ long mem = strtol (p, NULL, 10);
+ print_mem (mem);
+ print_next = false;
+ continue;
+ }
+
+ if (!(p[0] == 'V' && p[1] == 'm'))
+ continue;
+
+ if (strcmp (&p[2], "Peak:") == 0)
+ fprintf (stderr, "VM Peak: ");
+ else if (strcmp (&p[2], "Size:") == 0)
+ fprintf (stderr, "VM Current: ");
+ else if (strcmp (&p[2], "HWM:") == 0)
+ fprintf (stderr, "RSS Peak: ");
+ else if (strcmp (&p[2], "RSS:") == 0)
+ fprintf (stderr, "RSS Current: ");
+ else
+ continue;
+
+ print_next = true;
+ }
+ fclose (s);
+ }
+}
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+
+/* Where to longjmp on OOM. */
+static jmp_buf oom_buf;
+
+/* Handle OOM situation. If handling more than one file, we might
+ just fail to handle some large file due to OOM, but could very well
+ handle other smaller files after it. */
+void
+dwz_oom (void)
+{
+ longjmp (oom_buf, 1);
+}
+
+/* General obstack for struct dw_cu, dw_die, also used for temporary
+ vectors. */
+static struct obstack ob;
+/* Short lived obstack, global only to free it on allocation failures. */
+static struct obstack ob2;
+
+/* After read_multifile ob and ob2 are moved over to these variables
+ and restored during final cleanup. */
+static struct obstack alt_ob, alt_ob2;
+
+static bool odr_active_p = false;
+
+/* Struct to gather statistics. */
+struct stats
+{
+ const char *file;
+ unsigned int root_cnt;
+ unsigned int namespace_cnt;
+ unsigned int lower_toplevel;
+ unsigned int die_count;
+ unsigned int lower_toplevel_with_checksum;
+ unsigned int dup_cnt;
+ unsigned int dup_chain_cnt;
+ unsigned int dup_chain_max_length;
+ unsigned int part_cnt;
+ unsigned int pu_ph1_cnt;
+ unsigned int pu_ph2_cnt;
+ unsigned int pu_toplevel_die_cnt;
+};
+static struct stats *stats;
+
+/* Initialize stats struct. */
+static void
+init_stats (const char *file)
+{
+ if (stats == NULL)
+ stats = (struct stats *)malloc (sizeof (*stats));
+ memset (stats, 0, sizeof (*stats));
+ stats->file = file;
+}
+
+/* Print stats struct, parsing statistics. */
+static void
+print_parse_stats (void)
+{
+ if (stats == NULL || stats->file == NULL)
+ return;
+
+ fprintf (stderr, "Parse statistics for %s\n", stats->file);
+
+ fprintf (stderr, "root_count : %10u\n",
+ stats->root_cnt);
+ fprintf (stderr, "namespace_count : %10u\n",
+ stats->namespace_cnt);
+ unsigned int upper_toplevel = stats->root_cnt + stats->namespace_cnt;
+ fprintf (stderr, "upper_toplevel : %10u\n",
+ upper_toplevel);
+ unsigned lower_toplevel
+ = stats->lower_toplevel + stats->lower_toplevel_with_checksum;
+ fprintf (stderr, "lower_toplevel : %10u\n",
+ lower_toplevel);
+ unsigned int toplevel = upper_toplevel + lower_toplevel;
+ fprintf (stderr, "toplevel : %10u\n",
+ toplevel);
+ unsigned non_toplevel = stats->die_count - toplevel;
+ fprintf (stderr, "non_toplevel : %10u\n",
+ non_toplevel);
+ fprintf (stderr, "die_count : %10u\n",
+ stats->die_count);
+}
+
+/* Print stats struct, dups statistics. */
+static void
+print_dups_stats (void)
+{
+ if (stats == NULL || stats->file == NULL)
+ return;
+
+ fprintf (stderr, "Duplicate statistics for %s\n", stats->file);
+
+ fprintf (stderr, "lower_toplevel with checksum : %10u\n",
+ stats->lower_toplevel_with_checksum);
+ fprintf (stderr, "dup_cnt : %10u\n",
+ stats->dup_cnt);
+ fprintf (stderr, "dup_chain_cnt : %10u\n",
+ stats->dup_chain_cnt);
+ fprintf (stderr, "average dup_chain length : %10.2f\n",
+ (double)stats->dup_cnt / (double)stats->dup_chain_cnt);
+ fprintf (stderr, "max dup_chain length : %10u\n",
+ stats->dup_chain_max_length);
+}
+
+static void
+print_part_stats (void)
+{
+ if (stats == NULL || stats->file == NULL)
+ return;
+
+ fprintf (stderr, "Partition statistics for %s\n", stats->file);
+
+ fprintf (stderr, "part_cnt : %10u\n", stats->part_cnt);
+ fprintf (stderr, "pu_ph1_cnt : %10u\n",
+ stats->pu_ph1_cnt);
+ fprintf (stderr, "pu_ph2_cnt : %10u\n",
+ stats->pu_ph2_cnt);
+ fprintf (stderr, "pu_cnt : %10u\n",
+ stats->pu_ph1_cnt + stats->pu_ph2_cnt);
+ fprintf (stderr, "pu_toplevel_die_cnt : %10u\n",
+ stats->pu_toplevel_die_cnt);
+}
+
+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)
+
+#define write_sleb128(ptr, val) \
+ do \
+ { \
+ int64_t valv = (val); \
+ bool more; \
+ do \
+ { \
+ unsigned char c = valv & 0x7f; \
+ valv >>= 7; \
+ more = ((valv != 0 || (c & 0x40) != 0) \
+ && (valv != -1 || (c & 0x40) == 0)); \
+ if (more) \
+ c |= 0x80; \
+ *ptr++ = c; \
+ } \
+ while (more); \
+ } \
+ while (0)
+
+/* Macro to skip a uleb128 or sleb128 number and update ptr to the end of the
+ number. */
+#define skip_leb128(ptr) \
+ do {} while ((*ptr++) & 0x80)
+
+/* Macro to parse a uint16_t value represented using form, return it and
+ update ptr to the end of the value at the same time. If the value doesn't
+ fit, assign true to error_p. */
+#define read_u16(ptr, form, error_p) \
+ ({ \
+ uint16_t ret = 0; \
+ switch (form) \
+ { \
+ case DW_FORM_data1: \
+ ret = read_8 (ptr); \
+ break; \
+ case DW_FORM_data2: \
+ ret = read_16 (ptr); \
+ break; \
+ case DW_FORM_data4: \
+ { \
+ uint32_t res = read_32 (ptr); \
+ ret = (uint16_t)res; \
+ if ((uint32_t)ret != res) \
+ error_p = true; \
+ break; \
+ } \
+ case DW_FORM_data8: \
+ { \
+ uint64_t res = read_64 (ptr); \
+ ret = (uint16_t)res; \
+ if ((uint64_t)ret != res) \
+ error_p = true; \
+ break; \
+ } \
+ case DW_FORM_udata: \
+ { \
+ uint64_t res = read_uleb128 (ptr); \
+ ret = (uint16_t)res; \
+ if ((uint64_t)ret != res) \
+ error_p = true; \
+ break; \
+ } \
+ case DW_FORM_sdata: \
+ { \
+ union { \
+ uint64_t u; \
+ int64_t i; \
+ } res; \
+ res.u = read_sleb128 (ptr); \
+ ret = (uint16_t)res.u; \
+ if (res.i < 0 || (uint64_t)ret != res.u) \
+ error_p = true; \
+ break; \
+ } \
+ default: \
+ error_p = true; \
+ break; \
+ } \
+ ret; \
+ })
+
+/* Pointer size in the debug info, in bytes. Only debug info
+ with a single pointer size are handled. */
+static int ptr_size;
+
+/* Lowest debug_line version we have seen. When writing out the multi
+ file .debug_line we'll only use a DWARF5 version when there is no
+ lower line table seen (since the debug_line dir and file table is
+ shared between all CUs). */
+static unsigned int lowest_line_version = 5;
+
+/* 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)
+ | ((unsigned int)data[3] << 24));
+}
+
+static inline uint32_t
+buf_read_ube32 (unsigned char *data)
+{
+ return (data[3] | (data[2] << 8) | (data[1] << 16)
+ | ((unsigned int)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 DW_FORM_* name. */
+static const char *
+get_DW_FORM_str (unsigned int form)
+{
+ const char *name = get_DW_FORM_name (form);
+ static char buf[9 + 3 * sizeof (int)];
+ if (name)
+ return name;
+ sprintf (buf, "DW_FORM_%u", form);
+ return buf;
+}
+
+/* Return a DW_OP_* name. */
+static const char *
+get_DW_OP_str (unsigned int op)
+{
+ const char *name = get_DW_OP_name (op);
+ static char buf[7 + 3 * sizeof (int)];
+ if (name)
+ return name;
+ sprintf (buf, "DW_OP_%u", op);
+ return buf;
+}
+
+/* Return a DW_AT_* name. */
+static const char *
+get_DW_AT_str (unsigned int at)
+{
+ const char *name = get_DW_AT_name (at);
+ static char buf[7 + 3 * sizeof (int)];
+ if (name)
+ return name;
+ sprintf (buf, "DW_AT_%u", at);
+ return buf;
+}
+
+/* Return a DW_UT_* name. */
+static const char *
+get_DW_UT_str (unsigned int ut)
+{
+ const char *name = get_DW_UT_name (ut);
+ static char buf[7 + 3 * sizeof (int)];
+ if (name)
+ return name;
+ sprintf (buf, "DW_UT_%u", ut);
+ return buf;
+}
+
+/* Retrun a DW_LNCT_* name. */
+static const char *
+get_DW_LNCT_str (unsigned int lnct)
+{
+ const char *name;
+ static char buf[9 + 3 * sizeof (int)];
+ switch (lnct)
+ {
+ case DW_LNCT_path: name = "DW_LNCT_path"; break;
+ case DW_LNCT_directory_index: name = "DW_LNCT_directory_index"; break;
+ case DW_LNCT_timestamp: name = "DW_LNCT_timestamp"; break;
+ case DW_LNCT_size: name = "DW_LNCT_size"; break;
+ case DW_LNCT_MD5: name = "DW_LNCT_MD5"; break;
+
+ default: name = 0; break;
+ }
+ if (name)
+ return name;
+ sprintf (buf, "DW_LNCT_%u", lnct);
+ return buf;
+}
+
+/* This must match the debug_sections array content
+ below. */
+enum debug_section_kind
+{
+ DEBUG_INFO,
+ DEBUG_ABBREV,
+ DEBUG_LINE,
+ DEBUG_STR,
+ DEBUG_MACRO,
+ DEBUG_TYPES,
+ DEBUG_ARANGES,
+ DEBUG_PUBNAMES,
+ DEBUG_PUBTYPES,
+ DEBUG_GNU_PUBNAMES,
+ DEBUG_GNU_PUBTYPES,
+ DEBUG_MACINFO,
+ DEBUG_LOC,
+ DEBUG_LOCLISTS,
+ DEBUG_FRAME,
+ DEBUG_RANGES,
+ DEBUG_RNGLISTS,
+ DEBUG_LINE_STR,
+ DEBUG_SUP,
+ DEBUG_GDB_SCRIPTS,
+ GDB_INDEX,
+ GNU_DEBUGALTLINK,
+ SECTION_COUNT,
+ SAVED_SECTIONS = DEBUG_TYPES + 1
+};
+
+/* Details about standard DWARF sections. */
+static struct
+{
+ const char *name;
+ unsigned char *data;
+ unsigned char *new_data;
+ size_t size;
+ size_t new_size;
+ int sec;
+} debug_sections[] =
+ {
+ { ".debug_info", NULL, NULL, 0, 0, 0 },
+ { ".debug_abbrev", NULL, NULL, 0, 0, 0 },
+ { ".debug_line", NULL, NULL, 0, 0, 0 },
+ { ".debug_str", NULL, NULL, 0, 0, 0 },
+ { ".debug_macro", NULL, NULL, 0, 0, 0 },
+ { ".debug_types", NULL, NULL, 0, 0, 0 },
+ { ".debug_aranges", NULL, NULL, 0, 0, 0 },
+ { ".debug_pubnames", NULL, NULL, 0, 0, 0 },
+ { ".debug_pubtypes", NULL, NULL, 0, 0, 0 },
+ { ".debug_gnu_pubnames", NULL, NULL, 0, 0, 0 },
+ { ".debug_gnu_pubtypes", NULL, NULL, 0, 0, 0 },
+ { ".debug_macinfo", NULL, NULL, 0, 0, 0 },
+ { ".debug_loc", NULL, NULL, 0, 0, 0 },
+ { ".debug_loclists", NULL, NULL, 0, 0, 0 },
+ { ".debug_frame", NULL, NULL, 0, 0, 0 },
+ { ".debug_ranges", NULL, NULL, 0, 0, 0 },
+ { ".debug_rnglists", NULL, NULL, 0, 0, 0 },
+ { ".debug_line_str", NULL, NULL, 0, 0, 0 },
+ { ".debug_sup", NULL, NULL, 0, 0, 0 },
+ { ".debug_gdb_scripts", NULL, NULL, 0, 0, 0 },
+ { ".gdb_index", NULL, NULL, 0, 0, 0 },
+ { ".gnu_debugaltlink", NULL, NULL, 0, 0, 0 },
+ { NULL, NULL, NULL, 0, 0, 0 }
+ };
+
+/* Copies of .new_data fields during write_multifile. */
+static unsigned char *saved_new_data[SAVED_SECTIONS];
+/* Copies of .new_size fields during write_multifile. */
+static size_t saved_new_size[SAVED_SECTIONS];
+
+/* Copies of .data fields after read_multifile. */
+static unsigned char *alt_data[SAVED_SECTIONS];
+/* Copies of .size fields after read_multifile. */
+static size_t alt_size[SAVED_SECTIONS];
+
+/* How many bytes of each of /tmp/dwz.debug_*.XXXXXX have we written
+ already. */
+static unsigned int multi_info_off, multi_abbrev_off;
+static unsigned int multi_line_off, multi_str_off;
+static unsigned int multi_macro_off;
+/* And corresponding file descriptors. */
+static int multi_info_fd = -1, multi_abbrev_fd = -1;
+static int multi_line_fd = -1, multi_str_fd = -1;
+static int multi_macro_fd = -1;
+
+/* Copy of one of the input file's ehdr. */
+static GElf_Ehdr multi_ehdr;
+
+/* Pointer size of all debug info sources accumulated during
+ write_multifile. */
+static int multi_ptr_size;
+/* And their endianity. */
+static int multi_endian;
+/* Highest .gdb_index version seen. */
+static unsigned int multi_gdb_index_ver;
+
+enum multifile_mode_kind
+{
+ MULTIFILE_MODE_WR = 1,
+ MULTIFILE_MODE_OP = 2,
+ MULTIFILE_MODE_RD = 4,
+ MULTIFILE_MODE_FI = 8,
+ MULTIFILE_MODE_LOW_MEM = 16
+};
+
+/* True while in write_multifile. */
+#define wr_multifile (multifile_mode & MULTIFILE_MODE_WR)
+
+/* True while in optimize_multifile. */
+#define op_multifile (multifile_mode & MULTIFILE_MODE_OP)
+
+/* True while in read_multifile. */
+#define rd_multifile (multifile_mode & MULTIFILE_MODE_RD)
+
+/* True while in finalize_multifile. */
+#define fi_multifile (multifile_mode & MULTIFILE_MODE_FI)
+
+/* True if running in low_mem mode. */
+#define low_mem (multifile_mode & MULTIFILE_MODE_LOW_MEM)
+
+/* SHA1 checksum (build-id) of the common file. */
+static unsigned char multifile_sha1[0x14];
+
+/* 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 first 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 first 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;
+ /* The values of DW_FORM_implicit_const attribute forms. */
+ int64_t *values;
+ /* Attribute/form pairs. */
+ struct abbrev_attr attr[0];
+};
+
+typedef struct dw_die *dw_die_ref;
+typedef struct dw_cu *dw_cu_ref;
+struct import_cu;
+
+/* An entry from .debug_line file table. */
+struct dw_file
+{
+ char *dir;
+ char *file;
+ uint64_t time, size;
+ unsigned int file_angle_brackets_encapsulated_no_slash : 1;
+};
+
+/* Internal representation of a compilation (or partial)
+ unit. */
+struct dw_cu
+{
+ /* Cached entries from .debug_line file table. */
+ struct dw_file *cu_files;
+ unsigned int cu_nfiles;
+ /* Kind of CU, normal (present in .debug_info), newly created
+ partial unit, .debug_types unit or .debug_info partial unit
+ from the common file. */
+ enum { CU_NORMAL, CU_PU, CU_TYPES, CU_ALT } cu_kind;
+ /* CUs linked from first_cu through this chain. */
+ dw_cu_ref cu_next;
+ /* Offset in original .debug_info if CU_NORMAL or .debug_types
+ if CU_TYPES, 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;
+ /* The original abbreviation hash table. */
+ htab_t cu_abbrev;
+ /* 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. */
+ dw_cu_ref 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;
+ /* When op_multifile, record which object this came from here,
+ otherwise it is the index of the CU. */
+ unsigned int cu_chunk;
+ /* Form chosen for intra-cu references. */
+ enum dwarf_form cu_intracu_form;
+ /* Intracusize argument to init_new_die_offsets. Set in compute_abbrevs,
+ used in recompute_abbrevs. */
+ unsigned int initial_intracusize;
+ enum dwarf_source_language lang;
+};
+
+/* 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
+{
+ /* Offset in old .debug_info from the start of the .debug_info section,
+ -1U for newly created DIEs. */
+ unsigned int die_offset;
+ /* 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;
+ /* Set if DIE is unsuitable for moving into alternate .debug_info. */
+ unsigned int die_no_multifile : 1;
+ /* Set if DIE is referenced using DW_FORM_ref*. So far only used during
+ optimize_multifile and low_mem. */
+ unsigned int die_referenced : 1;
+ /* Set if DIE is referenced using DW_FORM_ref_addr. So far used only
+ during low_mem. */
+ unsigned int die_intercu_referenced : 1;
+ /* Set if DIE has its children collapsed. Only used during
+ optimize_multifile. */
+ unsigned int die_collapsed_children : 1;
+ /* Set on collapsed child DIE that is referenced. In that case, die_tag
+ is reused for die_enter difference from parent and no fields after
+ die_parent are allocated. */
+ unsigned int die_collapsed_child : 1;
+ /* Set if die_parent field is reused for struct dw_cu pointer. */
+ unsigned int die_root : 1;
+ /* State for ODR optimization. */
+ enum { ODR_UNKNOWN, ODR_NONE, ODR_DEF, ODR_DECL } die_odr_state : 2;
+ /* Tree pointer to parent. */
+ dw_die_ref die_parent;
+
+ /* The remaining fields are present only if die_collapsed_child is
+ 0. */
+
+ /* Tree pointers, to first child and pointer to next sibling. */
+ dw_die_ref die_child, die_sib;
+ /* Pointer to the old .debug_abbrev entry's internal representation. */
+ struct abbrev_tag *die_abbrev;
+ /* 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;
+ /* 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 partition_dups
+ and functions they call). */
+ 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;
+ /* For ODR phase 1, we change die_hash for ODR_DEF and ODR_DECL DIEs
+ to only hash in the tag and the name, to be able to construct
+ maximal duplicate chains. But during ODR phase 2, we want to
+ compare ODR_DEF DIEs in the normal way, for which we need the
+ unchanged die_hash, which we store here in die_hash2. */
+ hashval_t die_hash2;
+ /* 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 (die)->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;
+
+ /* The remaining fields are present only if die_toplevel is
+ 1. */
+
+ /* 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;
+};
+
+#include "iterators.h"
+
+/* Return CU structure pointer for a DIE. In order to save memory,
+ individual DIEs don't have a dw_cu_ref field, and the pointer can
+ be only found by overriding the die_parent pointer in a
+ DW_TAG_{compile,partial}_unit descriptor, which has no parent. */
+static inline dw_cu_ref
+die_cu (dw_die_ref die)
+{
+ while (!die->die_root)
+ die = die->die_parent;
+ return (dw_cu_ref) die->die_parent;
+}
+
+/* Given a toplevel die DIE, return the first (that is, the reference die) in
+ the duplicate chain. */
+#define first_dup(die) \
+ (die->die_dup \
+ ? die->die_dup \
+ : (die->die_nextdup \
+ ? die \
+ : NULL))
+
+/* Safe variant that check die_toplevel. Can't be used on LHS. */
+#define die_safe_dup(die) \
+ ((die)->die_toplevel ? (die)->die_dup : (dw_die_ref) NULL)
+#define die_safe_nextdup(die) \
+ ((die)->die_toplevel ? (die)->die_nextdup : (dw_die_ref) NULL)
+
+ALIGN_STRUCT (abbrev_tag)
+ALIGN_STRUCT (dw_file)
+ALIGN_STRUCT (dw_cu)
+ALIGN_STRUCT (dw_die)
+
+
+/* After read_multifile, pool variable is moved over to this variable
+ as the pool from read_multifile needs to be around for subsequent dwz
+ calls. Freed only during the final cleanup at the very end. */
+static unsigned char *alt_pool;
+
+static struct abbrev_tag *
+pool_clone_abbrev (struct abbrev_tag *t)
+{
+ struct abbrev_tag *newt;
+ size_t newt_size;
+ unsigned nvalue = 0;
+ if (t->values != NULL)
+ {
+ unsigned i;
+ for (i = 0; i < t->nattr; i++)
+ if (t->attr[i].form == DW_FORM_implicit_const)
+ nvalue = i + 1;
+ }
+ newt_size = (sizeof (*newt)
+ + t->nattr * sizeof (struct abbrev_attr)
+ + nvalue * sizeof (int64_t));
+ newt = pool_alloc (abbrev_tag, newt_size);
+ memcpy (newt, t, newt_size - (nvalue * sizeof (int64_t)));
+ if (nvalue == 0)
+ newt->values = NULL;
+ else
+ {
+ newt->values = (int64_t *) &newt->attr[newt->nattr];
+ memcpy (newt->values, t->values, nvalue * sizeof (int64_t));
+ }
+ return newt;
+}
+
+/* Hash function in first abbrev hash table 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 first 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;
+}
+
+/* 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
+ || (t1->attr[i].form == DW_FORM_implicit_const
+ && t1->values[i] != t2->values[i]))
+ 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;
+
+ hash_init_state ();
+ hash_update_state_object (t->tag);
+ hash_update_state_object (t->nattr);
+ hash_update_state_object (t->children);
+ for (i = 0; i < t->nattr; i++)
+ {
+ hash_update_state_object (t->attr[i].attr);
+ hash_update_state_object (t->attr[i].form);
+ if (t->attr[i].form == DW_FORM_implicit_const)
+ hash_update_state_object (t->values[i]);
+ }
+ t->hash = hash_digest ();
+}
+
+/* Maximum number of attributes in a DIE. */
+static unsigned int max_nattr;
+
+/* Parse a .debug_abbrev entry at PTR. */
+static htab_t
+read_abbrev (DSO *dso, unsigned char *ptr)
+{
+ htab_t h;
+ unsigned int attr, form;
+ struct abbrev_tag *t;
+ void **slot;
+
+ h = htab_try_create (50, abbrev_hash, abbrev_eq, NULL);
+ if (h == NULL)
+ dwz_oom ();
+
+ while ((attr = read_uleb128 (ptr)) != 0)
+ {
+ int highest_implicit_value_ndx = -1;
+ unsigned int nattr = 0;
+ unsigned char *p = ptr;
+
+ skip_leb128 (p);
+ p++;
+ while (read_uleb128 (p) != 0)
+ {
+ nattr++;
+ form = read_uleb128 (p);
+ if (form == DW_FORM_implicit_const)
+ {
+ skip_leb128 (p);
+ highest_implicit_value_ndx = nattr - 1;
+ }
+ else if (form == 2
+ || (form > DW_FORM_flag_present
+ && !(form == DW_FORM_ref_sig8
+ || form == DW_FORM_data16
+ || form == DW_FORM_line_strp)))
+ {
+ error (0, 0, "%s: Unknown DWARF %s at .debug_abbrev [%zd]",
+ dso->filename, get_DW_FORM_str (form),
+ p - debug_sections[DEBUG_ABBREV].data);
+ htab_delete (h);
+ return NULL;
+ }
+ }
+ if (read_uleb128 (p) != 0)
+ {
+ error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros",
+ dso->filename);
+ htab_delete (h);
+ return NULL;
+ }
+
+ t = pool_alloc (abbrev_tag,
+ sizeof (*t) + nattr * sizeof (struct abbrev_attr)
+ + sizeof (int64_t) * (highest_implicit_value_ndx + 1));
+ t->entry = attr;
+ t->hash = attr;
+ t->nattr = nattr;
+ t->nusers = 0;
+ t->tag = read_uleb128 (ptr);
+ t->children = *ptr++ == DW_CHILDREN_yes;
+ t->op_type_referenced = false;
+ t->values = (highest_implicit_value_ndx >= 0
+ ? (int64_t *) &t->attr[nattr] : NULL);
+ nattr = 0;
+ while ((attr = read_uleb128 (ptr)) != 0)
+ {
+ form = read_uleb128 (ptr);
+ if (form == DW_FORM_implicit_const)
+ t->values[nattr] = read_sleb128 (ptr);
+ t->attr[nattr].attr = attr;
+ t->attr[nattr++].form = form;
+ }
+ skip_leb128 (ptr);
+ if (t->nattr > max_nattr)
+ max_nattr = t->nattr;
+ slot = htab_find_slot_with_hash (h, t, t->hash, INSERT);
+ if (slot == NULL)
+ {
+ htab_delete (h);
+ dwz_oom ();
+ }
+ if (*slot != NULL)
+ {
+ error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename,
+ t->entry);
+ htab_delete (h);
+ return NULL;
+ }
+ *slot = t;
+ }
+
+ return h;
+}
+
+/* For a die attribute with form FORM starting at PTR, with the die in CU,
+ return the pointer after the attribute, assuming FORM is not
+ dw_form_indirect. */
+static inline unsigned char * FORCE_INLINE
+skip_attr_no_dw_form_indirect (unsigned int cu_version, uint32_t form,
+ unsigned char *ptr)
+{
+ size_t len = 0;
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ ptr += cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_addr:
+ ptr += ptr_size;
+ break;
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ 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:
+ case DW_FORM_line_strp:
+ ptr += 4;
+ break;
+ case DW_FORM_ref8:
+ case DW_FORM_data8:
+ case DW_FORM_ref_sig8:
+ ptr += 8;
+ break;
+ case DW_FORM_data16:
+ ptr += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_udata:
+ skip_leb128 (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 ptr;
+}
+
+/* Read the directory and file table from .debug_line offset OFF,
+ record it in CU. */
+static int
+read_debug_line (DSO *dso, dw_cu_ref 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;
+ unsigned int culen;
+ uint32_t value, version, ndirs, nfiles, dirt_cnt, file_cnt;
+ /* DWARF5 has a dynamic table of elements in possible different
+ forms. But we are only interested in the known elements (path,
+ dir index, time, size and possibly later md5). */
+ unsigned char n, nelems = 0;
+ int path_ndx = -1;
+ int dir_ndx = -1;
+ int time_ndx = -1;
+ int size_ndx = -1;
+ uint16_t elems[256];
+
+ 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;
+ culen = read_32 (ptr);
+ if (culen >= 0xfffffff0)
+ {
+ error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+ return 1;
+ }
+ endcu += culen;
+
+ 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 > 5)
+ {
+ error (0, 0, "%s: DWARF version %d in .debug_line unhandled",
+ dso->filename, value);
+ return 1;
+ }
+ version = value;
+
+ if (version < lowest_line_version)
+ lowest_line_version = version;
+
+ if (version >= 5)
+ {
+ int addr_size, seg_size;
+ if (ptr + 2 > endcu)
+ {
+ error (0, 0, "%s: .debug_line header too short", dso->filename);
+ return 1;
+ }
+ addr_size = *ptr++;
+ seg_size = *ptr++;
+ if (addr_size != ptr_size)
+ {
+ error (0, 0, "%s: .debug_line address size differs from CU ptr size",
+ dso->filename);
+ return 1;
+ }
+ if (seg_size != 0)
+ {
+ error (0, 0, "%s: .debug_line non-zero segment selector size",
+ dso->filename);
+ 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 + (version >= 4)];
+ ptr = dir = ptr + 4 + (version >= 4) + opcode_base;
+
+ /* dir table: */
+ if (version < 5)
+ {
+ value = 1;
+ while (*ptr != 0)
+ {
+ ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+ ++value;
+ }
+ ndirs = value;
+ }
+ else
+ {
+ nelems = *ptr++;
+ for (n = 0; n < nelems; n++)
+ {
+ uint16_t lnct = read_uleb128 (ptr);
+ uint16_t form = read_uleb128 (ptr);
+ if (lnct == DW_LNCT_path)
+ {
+ if (path_ndx != -1)
+ {
+ error (0, 0, "%s: .debug_line duplicate dir path elements",
+ dso->filename);
+ return 1;
+ }
+ path_ndx = n;
+ }
+ else
+ {
+ error (0, 0, "%s: .debug_line unhandled dir element %s",
+ dso->filename, get_DW_LNCT_str (lnct));
+ return 1;
+ }
+
+ if (form != DW_FORM_string
+ && form != DW_FORM_strp
+ && form != DW_FORM_line_strp)
+ {
+ error (0, 0, "%s: .debug_line unhandled form %s for dir path",
+ dso->filename, get_DW_FORM_str (form));
+ return 1;
+ }
+
+ elems[n] = form;
+ }
+
+ ndirs = read_uleb128 (ptr);
+ }
+
+ dirt = (unsigned char **) alloca (ndirs * sizeof (unsigned char *));
+ if (version < 5)
+ {
+ dirt[0] = NULL;
+ dirt_cnt = 1;
+ ptr = dir;
+ while (*ptr != 0)
+ {
+ dirt[dirt_cnt++] = ptr;
+ ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+ }
+ ptr++;
+ }
+ else
+ {
+ for (dirt_cnt = 0; dirt_cnt < ndirs; dirt_cnt++)
+ {
+ for (n = 0; n < nelems; n++)
+ {
+ uint32_t form = elems[n];
+ if (n == path_ndx)
+ {
+ unsigned char *d;
+ switch (form)
+ {
+ case DW_FORM_string:
+ d = (unsigned char *) ptr;
+ break;
+ case DW_FORM_strp:
+ {
+ unsigned int strp = do_read_32 (ptr);
+ if (strp >= debug_sections[DEBUG_STR].size)
+ d = NULL;
+ else
+ d = ((unsigned char *)
+ debug_sections[DEBUG_STR].data
+ + strp);
+ }
+ break;
+ case DW_FORM_line_strp:
+ {
+ unsigned int line_strp = do_read_32 (ptr);
+ if (line_strp >= debug_sections[DEBUG_LINE_STR].size)
+ d = NULL;
+ else
+ d = ((unsigned char *)
+ debug_sections[DEBUG_LINE_STR].data
+ + line_strp);
+ }
+ break;
+ default:
+ d = NULL;
+ break;
+ }
+
+ if (d == NULL)
+ {
+ error (0, 0, "%s: .debug_line bad dir path",
+ dso->filename);
+ return 1;
+ }
+
+ /* Note we do this even for the zero entry, which is
+ marked as NULL for pre-DWARF5 line tables. This
+ is important for when we merge file entries
+ together for a multifile because the zero dir
+ entry could differ. It is should be equivalent
+ to the CU DIE comp_dir attribute, but we don't
+ track that all CUs referring to the (same) line
+ table share identical an DW_AT_comp_dir value. */
+ dirt[dirt_cnt] = d;
+ }
+ ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr);
+ }
+ }
+ }
+
+ /* file table: */
+ file = ptr;
+ if (version < 5)
+ {
+ 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;
+ }
+
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ file_cnt++;
+ }
+ nfiles = file_cnt;
+ }
+ else
+ {
+ nelems = *ptr++;
+ path_ndx = -1;
+ for (n = 0; n < nelems; n++)
+ {
+ uint16_t lnct = read_uleb128 (ptr);
+ uint16_t form = read_uleb128 (ptr);
+ switch (lnct)
+ {
+ case DW_LNCT_path:
+ if (path_ndx != -1)
+ {
+ error (0, 0,
+ "%s: .debug_line duplicate file path elements",
+ dso->filename);
+ return 1;
+ }
+ path_ndx = n;
+
+ /* Currently we only handle two string form which always
+ stay... */
+ if (form != DW_FORM_string && form != DW_FORM_line_strp)
+ {
+ error (0, 0,
+ "%s: .debug_line unhandled form %s for file path",
+ dso->filename, get_DW_FORM_str (form));
+ return 1;
+ }
+ break;
+
+ case DW_LNCT_directory_index:
+ if (dir_ndx != -1)
+ {
+ error (0, 0,
+ "%s: .debug_line duplicate file dir elements",
+ dso->filename);
+ return 1;
+ }
+ dir_ndx = n;
+
+ if (form != DW_FORM_data1
+ && form != DW_FORM_data2
+ && form != DW_FORM_udata)
+ {
+ error (0, 0,
+ "%s: .debug_line unhandled form %s for dir index",
+ dso->filename, get_DW_FORM_str (form));
+ return 1;
+ }
+ break;
+
+ case DW_LNCT_timestamp:
+ if (time_ndx != -1)
+ {
+ error (0, 0,
+ "%s: .debug_line duplicate file time elements",
+ dso->filename);
+ return 1;
+ }
+ time_ndx = n;
+
+ if (form != DW_FORM_udata
+ && form != DW_FORM_data4
+ && form != DW_FORM_data8)
+ {
+ error (0, 0,
+ "%s: .debug_line unhandled form %s for file time",
+ dso->filename, get_DW_FORM_str (form));
+ return 1;
+ }
+ break;
+
+ case DW_LNCT_size:
+ if (size_ndx != -1)
+ {
+ error (0, 0,
+ "%s: .debug_line duplicate file size elements",
+ dso->filename);
+ return 1;
+ }
+ size_ndx = n;
+
+ if (form != DW_FORM_udata
+ && form != DW_FORM_data1
+ && form != DW_FORM_data2
+ && form != DW_FORM_data4
+ && form != DW_FORM_data8)
+ {
+ error (0, 0,
+ "%s: .debug_line unhandled form %s for file size",
+ dso->filename, get_DW_FORM_str (form));
+ return 1;
+ }
+ break;
+
+ default:
+ error (0, 0, "%s: .debug_line unhandled file element %s",
+ dso->filename, get_DW_LNCT_str (lnct));
+ return 1;
+ }
+ elems[n] = form;
+ }
+
+ nfiles = read_uleb128 (ptr);
+ if (nfiles > 0)
+ nfiles--; /* We will skip the first (zero) entry. */
+ }
+
+ cu->cu_nfiles = nfiles;
+ cu->cu_files = pool_alloc (dw_file, nfiles * sizeof (struct dw_file));
+ memset (cu->cu_files, 0, nfiles * sizeof (struct dw_file));
+
+ if (version < 5)
+ ptr = file;
+
+ for (file_cnt = 0; file_cnt < nfiles; file_cnt++)
+ {
+ char *f = NULL;
+ char *end = NULL;
+ uint32_t d = 0;
+ uint64_t time = 0;
+ uint64_t size = 0;
+ if (version < 5)
+ {
+ f = (char *) ptr;
+ ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+ end = (char *) ptr;
+ d = read_uleb128 (ptr);
+ time = read_uleb128 (ptr);
+ size = read_uleb128 (ptr);
+ }
+ else
+ {
+ /* Skip zero entry. */
+ if (file_cnt == 0)
+ for (n = 0; n < nelems; n++)
+ ptr = skip_attr_no_dw_form_indirect (cu->cu_version,
+ elems[n], ptr);
+
+ for (n = 0; n < nelems; n++)
+ {
+ uint32_t form = elems[n];
+ if (n == path_ndx)
+ {
+ switch (form)
+ {
+ case DW_FORM_string:
+ f = (char *) ptr;
+ end = strchr ((char *)ptr, 0) + 1;
+ break;
+ case DW_FORM_strp:
+ {
+ unsigned int strp = do_read_32 (ptr);
+ if (strp >= debug_sections[DEBUG_STR].size)
+ f = NULL;
+ else
+ {
+ f = ((char *) debug_sections[DEBUG_STR].data
+ + strp);
+ end = f + strlen (f) + 1;
+ }
+ }
+ break;
+ case DW_FORM_line_strp:
+ {
+ unsigned int line_strp = do_read_32 (ptr);
+ if (line_strp >= debug_sections[DEBUG_LINE_STR].size)
+ f = NULL;
+ else
+ {
+ f = ((char *) debug_sections[DEBUG_LINE_STR].data
+ + line_strp);
+ end = f + strlen (f) + 1;
+ }
+ }
+ break;
+ default:
+ f = NULL;
+ break;
+ }
+
+ if (f == NULL)
+ {
+ error (0, 0, "%s: .debug_line bad file path",
+ dso->filename);
+ return 1;
+ }
+ }
+ else if (n == dir_ndx)
+ {
+ switch (form)
+ {
+ case DW_FORM_data1:
+ d = *ptr;
+ break;
+ case DW_FORM_data2:
+ d = do_read_16 (ptr);
+ break;
+ case DW_FORM_udata:
+ {
+ unsigned char *p = ptr;
+ d = read_uleb128 (p);
+ }
+ break;
+ }
+ }
+ ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr);
+ }
+ }
+
+ cu->cu_files[file_cnt].file = f;
+ if (d >= dirt_cnt)
+ {
+ error (0, 0, "%s: Wrong directory table index %u",
+ dso->filename, value);
+ return 1;
+ }
+
+ cu->cu_files[file_cnt].dir = (char *) dirt[d];
+ cu->cu_files[file_cnt].time = time;
+ cu->cu_files[file_cnt].size = size;
+ size_t file_len = (char *) end - f;
+ size_t strlen_file = file_len - 1;
+ bool file_has_slash = false;
+ if (cu->cu_files[file_cnt].file[0] != '/'
+ && cu->cu_files[file_cnt].dir != NULL)
+ {
+ size_t dir_len = strlen (cu->cu_files[file_cnt].dir);
+ if (dir_len)
+ {
+ obstack_grow (&ob, cu->cu_files[file_cnt].dir,
+ dir_len);
+ strlen_file += dir_len;
+ if (cu->cu_files[file_cnt].dir[dir_len - 1] != '/')
+ {
+ obstack_1grow (&ob, '/');
+ strlen_file++;
+ }
+ file_has_slash = true;
+ obstack_grow (&ob, cu->cu_files[file_cnt].file,
+ file_len);
+ cu->cu_files[file_cnt].file
+ = (char *) obstack_finish (&ob);
+ cu->cu_files[file_cnt].dir = NULL;
+ }
+ }
+ cu->cu_files[file_cnt].file_angle_brackets_encapsulated_no_slash
+ = (!file_has_slash
+ && cu->cu_files[file_cnt].file[0] == '<'
+ && cu->cu_files[file_cnt].file[strlen_file - 1] == '>'
+ && strchr (cu->cu_files[file_cnt].file, '/') == NULL);
+ }
+
+ return 0;
+}
+
+/* Estimate the amount of DIEs in the .debug_info section, based on the size
+ of that section. */
+static unsigned int
+estimate_nr_dies (void)
+{
+ unsigned int average_die_size = 11;
+ unsigned int nr_dies = debug_sections[DEBUG_INFO].size / average_die_size;
+ return nr_dies;
+}
+
+static size_t
+emulate_htab (size_t initial, size_t final_nr_elements)
+{
+ size_t size = initial;
+
+ /* Emulate creation. */
+ size = higher_prime_number (size);
+
+ /* Emulate growing till htab contains find_nr_elements. */
+ while (1)
+ {
+ /* Emulate expansion trigger. */
+ size_t nr_elements = size * 3 / 4;
+ while (!(size * 3 <= nr_elements * 4))
+ nr_elements++;
+
+ if (nr_elements > final_nr_elements)
+ {
+ nr_elements = final_nr_elements;
+ break;
+ }
+
+ /* Emulate expansion. */
+ size = size * 2;
+ size = higher_prime_number (size);
+ }
+
+ return size;
+}
+
+/* Print hash table statistics for hash table HTAB with message string MSG. */
+static void
+htab_report (htab_t htab, const char *msg)
+{
+ double collisions = htab_collisions (htab);
+ unsigned int searches = htab->searches;
+ size_t elements = htab->n_elements;
+ size_t deleted = htab->n_deleted;
+ size_t adjusted_elements = elements - deleted;
+ size_t size = htab->size;
+ double occupancy = (double)elements / (double)size;
+ double adjusted_occupancy = (double)adjusted_elements / (double)size;
+ /* Indent unconditional fprintfs similar to conditional fprintfs to
+ left-align literal strings. */
+ if (1)
+ fprintf (stderr, "htab: %s\n", msg);
+ if (1)
+ fprintf (stderr, " size: %zu\n", size);
+ if (elements > 0 && deleted == 0)
+ fprintf (stderr, " elements: %zu, occupancy: %f\n", elements,
+ occupancy);
+ if (deleted > 0)
+ fprintf (stderr, " elements (incl. deleted): %zu, occupancy: %f\n",
+ elements, occupancy);
+ if (deleted > 0)
+ fprintf (stderr, " elements (excl. deleted): %zu, occupancy: %f\n",
+ adjusted_elements, adjusted_occupancy);
+ if (elements > 0)
+ fprintf (stderr, " searches: %u, collisions: %f\n", searches,
+ collisions);
+}
+
+/* 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 / 6;
+}
+
+/* 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;
+
+/* After read_multifile off_htab is copied over to this variable.
+ Offsets in the alternate .debug_info are found using this hash table. */
+static htab_t alt_off_htab;
+
+/* Offset hash table for .debug_types section. */
+static htab_t types_off_htab;
+
+/* Function to add DIE into the hash table (and create the hash table
+ when not already created). */
+static void
+off_htab_add_die (dw_cu_ref cu, dw_die_ref die, unsigned int *die_count)
+{
+ void **slot;
+
+ if (unlikely (cu->cu_kind == CU_TYPES))
+ {
+ if (types_off_htab == NULL)
+ {
+ types_off_htab = htab_try_create (100000, off_hash, off_eq, NULL);
+ if (types_off_htab == NULL)
+ dwz_oom ();
+ }
+
+ slot = htab_find_slot (types_off_htab, die, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ assert (*slot == NULL);
+ *slot = die;
+ return;
+ }
+
+ if (off_htab == NULL)
+ {
+ unsigned int estimated_nr_dies = estimate_nr_dies ();
+ size_t default_initial_size = 100000;
+ size_t initial_size;
+ if (low_mem
+ || op_multifile
+ || (multifile_mode == 0
+ && die_count_method == estimate
+ && (estimated_nr_dies >= low_mem_die_limit
+ || estimated_nr_dies >= max_die_limit)))
+ initial_size = default_initial_size;
+ else
+ {
+ size_t nr_dies;
+ if (die_count && *die_count != 0)
+ {
+ nr_dies = *die_count;
+ if (tracing)
+ fprintf (stderr, "Using die count %zu for off_htab"
+ " allocation\n", nr_dies);
+ }
+ else if (die_count_method == none)
+ nr_dies = 0;
+ else if (die_count_method == estimate)
+ {
+ nr_dies = estimated_nr_dies;
+ if (tracing)
+ fprintf (stderr, "Using die count estimate %zu for off_htab"
+ " allocation\n", nr_dies);
+ }
+ else
+ assert (false);
+
+ if (nr_dies != 0)
+ {
+ size_t final_hashtab_size
+ = emulate_htab (default_initial_size, nr_dies);
+ initial_size = final_hashtab_size;
+ }
+ else
+ initial_size = default_initial_size;
+ }
+ off_htab = htab_try_create (initial_size, off_hash, off_eq, NULL);
+ if (tracing)
+ htab_report (off_htab, "off_htab allocation");
+ if (off_htab == NULL)
+ dwz_oom ();
+ if (rd_multifile)
+ alt_off_htab = off_htab;
+ }
+
+ slot = htab_find_slot_with_hash (off_htab, die, off_hash (die), INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ assert (*slot == NULL);
+ *slot = die;
+}
+
+/* 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 (dw_cu_ref cu, unsigned int die_offset)
+{
+ struct dw_die die;
+ die.die_offset = die_offset;
+ if (cu == NULL)
+ return (dw_die_ref) htab_find_with_hash (off_htab, &die, off_hash (&die));
+ if (unlikely (cu->cu_kind == CU_ALT))
+ return (dw_die_ref) htab_find_with_hash (alt_off_htab, &die,
+ off_hash (&die));
+ if (unlikely (cu->cu_kind == CU_TYPES))
+ return (dw_die_ref) htab_find_with_hash (types_off_htab, &die,
+ off_hash (&die));
+ return (dw_die_ref) htab_find_with_hash (off_htab, &die, off_hash (&die));
+}
+
+/* For a die attribute ATTR starting at PTR, with the die in CU, return the
+ pointer after the attribute. */
+static inline unsigned char * FORCE_INLINE
+skip_attr (unsigned int cu_version, struct abbrev_attr *attr,
+ unsigned char *ptr)
+{
+ uint32_t form = attr->form;
+
+ while (form == DW_FORM_indirect)
+ form = read_uleb128 (ptr);
+ return skip_attr_no_dw_form_indirect (cu_version, form, ptr);
+}
+
+/* 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;
+ dw_cu_ref cu = die_cu (die);
+ if (unlikely (fi_multifile) && cu->cu_kind == CU_ALT)
+ ptr = alt_data[DEBUG_INFO];
+ else if (cu->cu_kind == CU_TYPES)
+ ptr = debug_sections[DEBUG_TYPES].data;
+ else
+ ptr = debug_sections[DEBUG_INFO].data;
+ ptr += die->die_offset;
+ skip_leb128 (ptr);
+ for (i = 0; i < t->nattr; ++i)
+ {
+ uint32_t form = t->attr[i].form;
+
+ while (form == DW_FORM_indirect)
+ form = read_uleb128 (ptr);
+ if (t->attr[i].attr == at)
+ {
+ *formp = form;
+ if (form == DW_FORM_implicit_const)
+ return (unsigned char *) &t->values[i];
+ return ptr;
+ }
+
+ ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr);
+ }
+ 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 *formp)
+{
+ unsigned char *ptr;
+ ptr = get_AT (die, at, formp);
+ *present = false;
+ if (ptr == NULL)
+ return 0;
+ *present = true;
+ switch (*formp)
+ {
+ case DW_FORM_ref_addr:
+ return read_size (ptr, die_cu (die)->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);
+ case DW_FORM_implicit_const:
+ return *(uint64_t *)ptr; /* See get_AT. */
+ 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 (unlikely (fi_multifile) && die_cu (die)->cu_kind == CU_ALT)
+ {
+ if (strp >= alt_size[DEBUG_STR])
+ return NULL;
+ return (char *) alt_data[DEBUG_STR] + strp;
+ }
+ if (strp >= debug_sections[DEBUG_STR].size)
+ return NULL;
+ return (char *) debug_sections[DEBUG_STR].data + strp;
+ }
+ case DW_FORM_line_strp:
+ {
+ unsigned int line_strp = read_32 (ptr);
+ if (line_strp >= debug_sections[DEBUG_LINE_STR].size)
+ return NULL;
+ else
+ return (char *) debug_sections[DEBUG_LINE_STR].data + line_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;
+ dw_cu_ref cu;
+
+ while (ptr < end)
+ {
+ op = *ptr++;
+ switch (op)
+ {
+ case DW_OP_addr:
+ die->die_no_multifile = 1;
+ 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);
+ cu = die_cu (die);
+ ref = off_htab_lookup (cu, cu->cu_offset + addr);
+ if (ref == NULL)
+ {
+ error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] "
+ "referenced by %s from DIE at [%x]",
+ dso->filename, cu->cu_offset + addr,
+ get_DW_OP_str (op), die->die_offset);
+ return 1;
+ }
+ if (op == DW_OP_call2)
+ ref->die_op_call2_referenced = 1;
+ if (ref->die_ck_state == CK_KNOWN)
+ {
+ dw_die_ref d;
+ ref->die_ck_state = CK_BAD;
+
+ d = ref;
+ while (!d->die_root
+ && d->die_parent->die_ck_state == CK_KNOWN)
+ {
+ d = d->die_parent;
+ d->die_ck_state = CK_BAD;
+ }
+ }
+ else
+ ref->die_ck_state = CK_BAD;
+ if (unlikely (low_mem))
+ {
+ ref->die_referenced = 1;
+ /* As .debug_loc adjustment is done after
+ write_info finishes, we need to keep the referenced
+ DIEs around uncollapsed. */
+ if (need_adjust)
+ ref->die_intercu_referenced = 1;
+ }
+ 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:
+ case DW_OP_implicit_pointer:
+ case DW_OP_GNU_variable_value:
+ cu = die_cu (die);
+ addr = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4);
+ if (cu->cu_version == 2)
+ ptr += ptr_size;
+ else
+ ptr += 4;
+ ref = off_htab_lookup (NULL, addr);
+ if (ref == NULL || (unlikely (low_mem) && ref->die_tag == 0))
+ {
+ error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] "
+ "referenced by %s from DIE at [%x]",
+ dso->filename, addr, get_DW_OP_str (op), die->die_offset);
+ return 1;
+ }
+ ref->die_no_multifile = 1;
+ if (unlikely (low_mem))
+ {
+ ref->die_referenced = 1;
+ /* As .debug_loc adjustment is done after
+ write_info finishes, we need to keep the referenced
+ DIEs around uncollapsed. */
+ if (die_cu (ref) != cu || need_adjust)
+ ref->die_intercu_referenced = 1;
+ }
+ die->die_ck_state = CK_BAD;
+ if (need_adjust)
+ *need_adjust = true;
+ if (op == DW_OP_GNU_implicit_pointer || op == DW_OP_implicit_pointer)
+ skip_leb128 (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:
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_bregx:
+ case DW_OP_bit_piece:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_implicit_value:
+ {
+ uint32_t leni = read_uleb128 (ptr);
+ ptr += leni;
+ }
+ break;
+ case DW_OP_GNU_entry_value:
+ case DW_OP_entry_value:
+ {
+ uint32_t leni = read_uleb128 (ptr);
+ if ((uint64_t) (end - ptr) < leni)
+ {
+ error (0, 0, "%s: %s with too large length",
+ get_DW_OP_str (op), 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_convert:
+ case DW_OP_GNU_reinterpret:
+ case DW_OP_reinterpret:
+ addr = read_uleb128 (ptr);
+ if (addr == 0)
+ break;
+ goto typed_dwarf;
+ case DW_OP_GNU_regval_type:
+ case DW_OP_regval_type:
+ skip_leb128 (ptr);
+ addr = read_uleb128 (ptr);
+ goto typed_dwarf;
+ case DW_OP_GNU_const_type:
+ case DW_OP_const_type:
+ addr = read_uleb128 (ptr);
+ ptr += *ptr + 1;
+ goto typed_dwarf;
+ case DW_OP_GNU_deref_type:
+ case DW_OP_deref_type:
+ ++ptr;
+ addr = read_uleb128 (ptr);
+ typed_dwarf:
+ cu = die_cu (die);
+ ref = off_htab_lookup (cu, cu->cu_offset + addr);
+ if (ref == NULL)
+ {
+ error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] "
+ "referenced by %s from DIE at [%x]",
+ dso->filename, cu->cu_offset + addr,
+ get_DW_OP_str (op), die->die_offset);
+ return 1;
+ }
+ if (unlikely (low_mem))
+ {
+ ref->die_referenced = 1;
+ /* As .debug_loc adjustment is done after
+ write_info finishes, we need to keep the referenced
+ DIEs around uncollapsed. */
+ if (need_adjust)
+ ref->die_intercu_referenced = 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 %s "
+ "referenced from DIE at [%x]",
+ dso->filename, get_DW_OP_str (op),
+ die->die_offset);
+ 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;
+}
+
+/* Add dummy die in CU at OFFSET. */
+static inline void FORCE_INLINE
+add_dummy_die (dw_cu_ref cu, unsigned int offset)
+{
+ dw_die_ref ref;
+ struct dw_die ref_buf;
+ void **slot;
+
+ memset (&ref_buf, '\0', offsetof (struct dw_die, die_child));
+ ref_buf.die_offset = offset;
+ ref_buf.die_collapsed_child = 1;
+ ref_buf.die_referenced = 1;
+ ref_buf.die_intercu_referenced = 1;
+ if (off_htab == NULL)
+ {
+ ref = pool_alloc (dw_die, offsetof (struct dw_die, die_child));
+ memcpy (ref, &ref_buf, offsetof (struct dw_die, die_child));
+ off_htab_add_die (cu, ref, NULL);
+ return;
+ }
+
+ slot
+ = htab_find_slot_with_hash (off_htab, &ref_buf, off_hash (&ref_buf),
+ INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot != NULL)
+ return;
+
+ ref = pool_alloc (dw_die, offsetof (struct dw_die, die_child));
+ memcpy (ref, &ref_buf, offsetof (struct dw_die, die_child));
+ *slot = (void *) ref;
+}
+
+/* Add dummy DIEs for expr_loc at PTR. */
+static int
+read_exprloc_low_mem_phase1 (DSO *dso, dw_die_ref die, unsigned char *ptr,
+ size_t len)
+{
+ unsigned char *end = ptr + len;
+ unsigned char op;
+ GElf_Addr addr;
+ dw_cu_ref cu;
+
+ 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)
+ read_16 (ptr);
+ else
+ read_32 (ptr);
+ break;
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ ptr += 4;
+ break;
+ case DW_OP_call_ref:
+ case DW_OP_GNU_implicit_pointer:
+ case DW_OP_implicit_pointer:
+ case DW_OP_GNU_variable_value:
+ cu = die_cu (die);
+ addr = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4);
+ if (cu->cu_version == 2)
+ ptr += ptr_size;
+ else
+ ptr += 4;
+ /* Adding a dummy DIE ref to mark an intercu reference is only
+ necessary if die_cu (ref) != cu, but we don't track cu's during
+ low-mem phase1. */
+ add_dummy_die (cu, addr);
+ if (op == DW_OP_GNU_implicit_pointer || op == DW_OP_implicit_pointer)
+ skip_leb128 (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:
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_bregx:
+ case DW_OP_bit_piece:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_implicit_value:
+ {
+ uint32_t leni = read_uleb128 (ptr);
+ ptr += leni;
+ }
+ break;
+ case DW_OP_GNU_entry_value:
+ case DW_OP_entry_value:
+ {
+ uint32_t leni = read_uleb128 (ptr);
+ if ((uint64_t) (end - ptr) < leni)
+ {
+ error (0, 0, "%s: %s with too large length",
+ get_DW_OP_str (op), dso->filename);
+ return 1;
+ }
+ if (read_exprloc_low_mem_phase1 (dso, die, ptr, leni))
+ return 1;
+ ptr += leni;
+ }
+ break;
+ case DW_OP_GNU_convert:
+ case DW_OP_convert:
+ case DW_OP_GNU_reinterpret:
+ case DW_OP_reinterpret:
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_GNU_regval_type:
+ case DW_OP_regval_type:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_GNU_const_type:
+ case DW_OP_const_type:
+ read_uleb128 (ptr);
+ ptr += *ptr + 1;
+ break;
+ case DW_OP_GNU_deref_type:
+ case DW_OP_deref_type:
+ ++ptr;
+ skip_leb128 (ptr);
+ break;
+ default:
+ error (0, 0, "%s: Unknown DWARF %s "
+ "referenced from DIE at [%x]",
+ dso->filename, get_DW_OP_str (op),
+ die->die_offset);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Add dummy DIEs for loclist at OFFSET. */
+static int
+read_loclist_low_mem_phase1 (DSO *dso, dw_cu_ref cu, dw_die_ref die,
+ GElf_Addr offset)
+{
+ unsigned char *ptr, *endsec;
+ GElf_Addr low, high;
+ size_t len = 0;
+ int sec;
+
+ sec = cu->cu_version < 5 ? DEBUG_LOC : DEBUG_LOCLISTS;
+ ptr = debug_sections[sec].data;
+ if (ptr == NULL)
+ {
+ error (0, 0, "%s: loclistptr attribute, yet no %s section",
+ dso->filename, debug_sections[sec].name);
+ return 1;
+ }
+ if (offset >= debug_sections[sec].size)
+ {
+ error (0, 0,
+ "%s: loclistptr offset %Ld outside of %s section",
+ dso->filename, (long long) offset, debug_sections[sec].name);
+ return 1;
+ }
+ endsec = ptr + debug_sections[sec].size;
+ ptr += offset;
+again:
+ while (ptr < endsec)
+ {
+ if (sec == DEBUG_LOC)
+ {
+ 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);
+ }
+ else
+ {
+ uint8_t lle = *ptr++;
+ switch (lle)
+ {
+ case DW_LLE_end_of_list:
+ goto done;
+
+ case DW_LLE_base_addressx:
+ skip_leb128 (ptr);
+ goto again;
+
+ case DW_LLE_startx_endx:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_startx_length:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_offset_pair:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_default_location:
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_base_address:
+ ptr += ptr_size;
+ goto again;
+
+ case DW_LLE_start_end:
+ ptr += 2 * ptr_size;
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_start_length:
+ ptr += ptr_size;
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_GNU_view_pair:
+ if (cu->cu_version != 5)
+ error (0, 0,
+ "%s: DW_LLE_GNU_view_pair used with DWARF version %u",
+ dso->filename, cu->cu_version);
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ goto again;
+
+ default:
+ error (0, 0,
+ "%s: unhandled location list entry 0x%x in %s section",
+ dso->filename, lle, debug_sections[sec].name);
+ return 1;
+ }
+ }
+
+ if (unlikely (!(ptr + len <= endsec)))
+ {
+ error (0, 0,
+ "%s: locexpr length 0x%Lx exceeds %s section",
+ dso->filename, (long long) len, debug_sections[sec].name);
+ return 1;
+ }
+
+ if (len > 0)
+ if (read_exprloc_low_mem_phase1 (dso, die, ptr, len))
+ return 1;
+
+ ptr += len;
+ }
+
+done:
+ return 0;
+}
+
+/* Add dummy dies for loc_exprs and loc_lists referenced from DIE. */
+static int
+add_locexpr_dummy_dies (DSO *dso, dw_cu_ref cu, dw_die_ref die,
+ unsigned char *ptr, uint32_t form, unsigned int attr,
+ size_t len)
+{
+ if (form == DW_FORM_block1 && cu->cu_version < 4)
+ {
+ /* Old DWARF uses blocks instead of exprlocs. */
+ switch (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_rank:
+ case DW_AT_call_value:
+ case DW_AT_call_target:
+ case DW_AT_call_target_clobbered:
+ case DW_AT_call_data_location:
+ case DW_AT_call_data_value:
+ 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 (read_exprloc_low_mem_phase1 (dso, die, ptr, len))
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+ }
+
+ if (form == DW_FORM_exprloc)
+ return read_exprloc_low_mem_phase1 (dso, die, ptr, len);
+
+ switch (attr)
+ {
+ 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 ((cu->cu_version < 4 && form == DW_FORM_data4)
+ || form == DW_FORM_sec_offset)
+ {
+ if (read_loclist_low_mem_phase1 (dso, cu, die, do_read_32 (ptr)))
+ return 1;
+ break;
+ }
+ else if (cu->cu_version < 4 && form == DW_FORM_data8)
+ {
+ if (read_loclist_low_mem_phase1 (dso, cu, die, do_read_64 (ptr)))
+ return 1;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ 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. */
+ dw_cu_ref cu;
+};
+ALIGN_STRUCT (debug_loc_adjust)
+
+/* Hash table and obstack for recording .debug_loc and .debug_loclists
+ adjustment ranges. */
+static htab_t loc_htab;
+static htab_t loclists_htab;
+
+/* Hash function for loc[lists]_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[lists]_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_cu_ref cu, dw_die_ref die, GElf_Addr offset)
+{
+ unsigned char *ptr, *endsec;
+ GElf_Addr low, high;
+ size_t len;
+ int sec;
+ bool need_adjust = false;
+
+ die->die_ck_state = CK_BAD;
+ sec = cu->cu_version < 5 ? DEBUG_LOC : DEBUG_LOCLISTS;
+ ptr = debug_sections[sec].data;
+ if (ptr == NULL)
+ {
+ error (0, 0, "%s: loclistptr attribute, yet no %s section",
+ dso->filename, debug_sections[sec].name);
+ return 1;
+ }
+ if (offset >= debug_sections[sec].size)
+ {
+ error (0, 0,
+ "%s: loclistptr offset %Ld outside of %s section",
+ dso->filename, (long long) offset, debug_sections[sec].name);
+ return 1;
+ }
+ endsec = ptr + debug_sections[sec].size;
+ ptr += offset;
+again:
+ while (ptr < endsec)
+ {
+ if (cu->cu_version < 5)
+ {
+ 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);
+ }
+ else
+ {
+ uint8_t lle = *ptr++;
+ switch (lle)
+ {
+ case DW_LLE_end_of_list:
+ goto done;
+
+ case DW_LLE_base_addressx:
+ skip_leb128 (ptr);
+ goto again;
+
+ case DW_LLE_startx_endx:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_startx_length:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_offset_pair:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_default_location:
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_base_address:
+ ptr += ptr_size;
+ goto again;
+
+ case DW_LLE_start_end:
+ ptr += 2 * ptr_size;
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_start_length:
+ ptr += ptr_size;
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_GNU_view_pair:
+ if (cu->cu_version != 5)
+ error (0, 0,
+ "%s: DW_LLE_GNU_view_pair used with DWARF version %u",
+ dso->filename, cu->cu_version);
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ goto again;
+
+ default:
+ error (0, 0,
+ "%s: unhandled location list entry 0x%x in %s section",
+ dso->filename, lle, debug_sections[sec].name);
+ return 1;
+ }
+ }
+
+ if (unlikely (!(ptr + len <= endsec)))
+ {
+ error (0, 0,
+ "%s: locexpr length 0x%Lx exceeds %s section",
+ dso->filename, (long long) len, debug_sections[sec].name);
+ return 1;
+ }
+
+ if (read_exprloc (dso, die, ptr, len, &need_adjust))
+ return 1;
+
+ ptr += len;
+ }
+
+done:
+ if (need_adjust)
+ {
+ struct debug_loc_adjust adj, *a;
+ void **slot;
+
+ adj.start_offset = offset;
+ adj.end_offset = ptr - debug_sections[sec].data;
+ adj.cu = cu;
+ if (sec == DEBUG_LOC)
+ {
+ if (loc_htab == NULL)
+ {
+ loc_htab = htab_try_create (50, loc_hash, loc_eq, NULL);
+ if (loc_htab == NULL)
+ dwz_oom ();
+ }
+ slot = htab_find_slot_with_hash (loc_htab, &adj, adj.end_offset,
+ INSERT);
+ }
+ else
+ {
+ if (loclists_htab == NULL)
+ {
+ loclists_htab = htab_try_create (50, loc_hash, loc_eq, NULL);
+ if (loclists_htab == NULL)
+ dwz_oom ();
+ }
+ slot = htab_find_slot_with_hash (loclists_htab, &adj, adj.end_offset,
+ INSERT);
+ }
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot == NULL)
+ {
+ a = pool_alloc (debug_loc_adjust, sizeof (*a));
+ *a = adj;
+ *slot = (void *) a;
+ }
+ else if (((struct debug_loc_adjust *)*slot)->cu != adj.cu)
+ {
+ error (0, 0, "%s: can't adjust %s section because multiple "
+ "CUs refer to it", dso->filename, debug_sections[sec].name);
+ return 1;
+ }
+ else if (((struct debug_loc_adjust *)*slot)->start_offset > offset)
+ ((struct debug_loc_adjust *)*slot)->start_offset = offset;
+ }
+
+ return 0;
+}
+
+/* Initialize die_odr_state field for DIE with CU. */
+static void
+set_die_odr_state (dw_cu_ref cu, dw_die_ref die)
+{
+ unsigned char *ptr;
+ struct abbrev_tag *t;
+ unsigned int i;
+ bool decl_p;
+ bool name_p;
+ bool other_p;
+
+ assert (die->die_odr_state == ODR_UNKNOWN);
+ die->die_odr_state = ODR_NONE;
+
+ if (low_mem)
+ /* Todo: allow low-mem mode. */
+ return;
+
+ if (multifile_mode == 0)
+ /* We're in regular mode, enable the ODR optimization. */
+ ;
+ else
+ /* One definition rule does not hold across executables and shared
+ libraries, so disable. */
+ return;
+
+ if (!die->die_toplevel)
+ /* A nested struct is not uniquely identified by its name. There may be a
+ different type with the same name nested in a different struct. */
+ return;
+
+ switch (cu->lang)
+ {
+ case DW_LANG_C_plus_plus:
+ case DW_LANG_C_plus_plus_03:
+ case DW_LANG_C_plus_plus_11:
+ case DW_LANG_C_plus_plus_14:
+ /* c++ defines one-definition-rule. */
+ if (die->die_tag == DW_TAG_structure_type
+ || die->die_tag == DW_TAG_class_type
+ || die->die_tag == DW_TAG_union_type)
+ /* ODR holds for all types, but we limit the optimization to these
+ tags, which are the ones likely to profit from it. */
+ ;
+ else
+ return;
+ break;
+ default:
+ return;
+ }
+
+ ptr = debug_sections[DEBUG_INFO].data + die->die_offset;
+ skip_leb128 (ptr);
+
+ t = die->die_abbrev;
+
+ decl_p = false;
+ name_p = false;
+ other_p = false;
+ for (i = 0; i < t->nattr; ++i)
+ {
+ if (t->attr[i].attr == DW_AT_name)
+ {
+ name_p = true;
+ continue;
+ }
+
+ if (t->attr[i].attr == DW_AT_declaration)
+ {
+ decl_p = true;
+ continue;
+ }
+
+ other_p = true;
+ }
+
+ if (!name_p)
+ /* Ignore anonymous types. */
+ return;
+
+ odr_active_p = true;
+
+ if (decl_p && !other_p && die->die_child == NULL)
+ {
+ /* Detected a declaration with no attributes other than DW_AT_name and
+ DW_AT_declaration, and no children. */
+ die->die_odr_state = ODR_DECL;
+ return;
+ }
+
+ die->die_odr_state = ODR_DEF;
+}
+
+/* Return the initialized die_odr_state field for DIE with CU. */
+static unsigned int
+die_odr_state (dw_die_ref die)
+{
+ assert (die->die_odr_state != ODR_UNKNOWN);
+ return die->die_odr_state;
+}
+
+/* This function computes u.p1.die_hash and die_ck_state of DIE.
+ The field u.p1.die_hash is an iterative hash of:
+ - the die_tag,
+ - for all attributes except DW_AT_sibling: the attribute code,
+ - for non-reference class attributes: the value of the attribute (magic
+ for DW_AT_*_file),
+ - for DW_FORM_ref_addr attributes: the value of the attribute,
+ - for reference class attributes that point into the subtree of TOP_DIE
+ (note, other references are intentionally ignored here):
+ ref->u.p1.die_enter - top_die->u.p1.die_enter,
+ - for all children: their hashes.
+ The field 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_cu_ref cu, 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;
+ bool only_hash_name_p;
+ hashval_t die_hash2;
+
+ 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_tag == DW_TAG_imported_unit)
+ die->die_ck_state = CK_BAD;
+ t = die->die_abbrev;
+ ptr = debug_sections[DEBUG_INFO].data + die->die_offset;
+ skip_leb128 (ptr);
+ s = die->die_tag;
+ die->u.p1.die_hash = iterative_hash_object (s, die->u.p1.die_hash);
+ if (dump_checksum_p)
+ fprintf (stderr, "DIE %x, hash: %x, tag\n", die->die_offset,
+ die->u.p1.die_hash);
+ if (uni_lang_p && die == top_die)
+ {
+ die->u.p1.die_hash
+ = iterative_hash_object (cu->lang, die->u.p1.die_hash);
+ if (dump_checksum_p)
+ fprintf (stderr, "DIE %x, hash: %x, lang\n", die->die_offset,
+ die->u.p1.die_hash);
+ }
+
+ if (odr && die->die_odr_state == ODR_UNKNOWN)
+ set_die_odr_state (die_cu (die), die);
+ only_hash_name_p = odr && die_odr_state (die) != ODR_NONE;
+ die_hash2 = 0;
+ if (only_hash_name_p)
+ die_hash2 = 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;
+ int64_t svalue;
+ 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:
+ case DW_AT_call_return_pc:
+ case DW_AT_call_pc:
+ die->die_ck_state = CK_BAD;
+ break;
+ case DW_AT_start_scope:
+ if (form == DW_FORM_sec_offset)
+ die->die_ck_state = CK_BAD;
+ break;
+ /* These attributes reference other sections, they
+ can't be moved to other files easily. */
+ case DW_AT_stmt_list:
+ case DW_AT_macro_info:
+ case DW_AT_macros:
+ case DW_AT_GNU_macros:
+ if (!die->die_root)
+ die->die_no_multifile = 1;
+ break;
+ /* loclistptr attributes. */
+ 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 ((cu->cu_version < 4 && form == DW_FORM_data4)
+ || form == DW_FORM_sec_offset)
+ {
+ if (read_loclist (dso, cu, die, read_32 (ptr)))
+ return 1;
+ ptr = old_ptr;
+ break;
+ }
+ else if (cu->cu_version < 4 && form == DW_FORM_data8)
+ {
+ if (read_loclist (dso, cu, 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;
+ case DW_FORM_sdata:
+ {
+ svalue = read_sleb128 (ptr);
+ if (svalue >= 0)
+ {
+ value = svalue;
+ handled = true;
+ break;
+ }
+ else
+ {
+ negative:
+ error (0, 0, "%s: negative value %" PRId64 " for %s",
+ dso->filename, svalue,
+ get_DW_AT_str (t->attr[i].attr));
+ return 1;
+ }
+ }
+ case DW_FORM_implicit_const:
+ {
+ svalue = t->values[i];
+ if (svalue >= 0)
+ {
+ value = svalue;
+ handled = true;
+ break;
+ }
+ else
+ goto negative;
+ }
+ default:
+ error (0, 0, "%s: Unhandled %s for %s",
+ dso->filename, get_DW_FORM_str (form),
+ get_DW_AT_str (t->attr[i].attr));
+ return 1;
+ }
+ if (handled)
+ {
+ unsigned char *new_ptr = ptr;
+ ptr = old_ptr;
+ if (value > cu->cu_nfiles)
+ {
+ error (0, 0, "%s: Invalid %s file number %d",
+ dso->filename, get_DW_AT_str (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 = &cu->cu_files[value - 1];
+ size_t file_len = strlen (cu_file->file);
+ s = t->attr[i].attr;
+ hash_init_state ();
+ hash_update_state_object (die->u.p1.die_hash);
+ hash_update_state_object (s);
+ hash_update_state_object (cu_file->time);
+ hash_update_state_object (cu_file->size);
+ hash_update_state (cu_file->file, file_len + 1);
+ if (cu_file->dir)
+ {
+ hash_update_state (cu_file->dir,
+ strlen (cu_file->dir) + 1);
+ }
+ /* 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_angle_brackets_encapsulated_no_slash
+ && 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)
+ {
+ die->u.p1.die_hash = hash_digest ();
+ break;
+ }
+
+ die->u.p1.die_hash = hash_digest ();
+
+ if (cu->cu_comp_dir
+ && (cu_file->dir ? cu_file->dir[0]
+ : cu_file->file[0]) != '/')
+ die->u.p1.die_hash
+ = iterative_hash (cu->cu_comp_dir,
+ strlen (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;
+ }
+ 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;
+ case DW_FORM_sdata:
+ {
+ svalue = read_sleb128 (ptr);
+ if (svalue >= 0)
+ {
+ value = svalue;
+ handled = true;
+ break;
+ }
+ else
+ goto negative;
+ }
+ case DW_FORM_implicit_const:
+ svalue = t->values[i];
+ if (svalue >= 0)
+ {
+ value = svalue;
+ handled = true;
+ break;
+ }
+ else
+ goto negative;
+ default:
+ error (0, 0, "%s: Unhandled %s for %s",
+ dso->filename, get_DW_FORM_str (form),
+ get_DW_AT_str (t->attr[i].attr));
+ return 1;
+ }
+ if (handled)
+ {
+ ptr = old_ptr;
+ 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 (value, die->u.p1.die_hash);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ if (unlikely (op_multifile || rd_multifile || fi_multifile))
+ {
+ dw_die_ref ref;
+
+ value = read_size (ptr, cu->cu_version == 2
+ ? ptr_size : 4);
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ 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);
+ }
+ ref = off_htab_lookup (cu, value);
+ if (ref == NULL)
+ {
+ error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] "
+ "referenced by %s from DIE at [%x]",
+ dso->filename, value,
+ get_DW_AT_str (t->attr[i].attr), die->die_offset);
+ return 1;
+ }
+ if (unlikely (op_multifile) && ref->die_collapsed_child)
+ ref = ref->die_parent;
+ if (cu == die_cu (ref))
+ {
+ /* The reference was encoded using a section-relative
+ encoding, while if it could have been encoded using
+ CU-relative encoding. Typically, the latter is used,
+ because:
+ - it's potentially smaller, and
+ - it doesn't require a link-time relocation. */
+
+ /* Assert that the multifile only contains section-relative
+ encoding when necessary. */
+ assert (!op_multifile && !rd_multifile);
+
+ if (fi_multifile)
+ {
+ /* It's possible that the input DWARF contains this
+ sub-optimal reference. We currently don't optimize
+ this during single-file optimization, so it will still
+ be there during finalize_multifile. Bail out to handle
+ this conservatively. */
+ die->die_ck_state = CK_BAD;
+ return 0;
+ }
+ }
+ /* Assert that during op_multifile, die belongs to the same object
+ as ref. */
+ assert (!op_multifile || cu->cu_chunk == die_cu (ref)->cu_chunk);
+ handled = true;
+ break;
+ }
+ die->die_no_multifile = 1;
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_addr:
+ die->die_no_multifile = 1;
+ ptr += ptr_size;
+ break;
+ case DW_FORM_flag_present:
+ break;
+ case DW_FORM_implicit_const:
+ if (!handled && die->die_ck_state != CK_BAD)
+ {
+ handled = true;
+ 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 (t->values[i], die->u.p1.die_hash);
+ }
+ 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:
+ ptr += 8;
+ break;
+ case DW_FORM_data16:
+ ptr += 16;
+ break;
+ case DW_FORM_ref_sig8:
+ die->die_no_multifile = 1;
+ ptr += 8;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ skip_leb128 (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 (cu, cu->cu_offset + value);
+ if (ref == NULL)
+ {
+ error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] "
+ "referenced by %s from DIE at [%x]",
+ dso->filename, cu->cu_offset + value,
+ get_DW_AT_str (t->attr[i].attr), die->die_offset);
+ 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->die_collapsed_child
+ && 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_strp:
+ if (unlikely (op_multifile || rd_multifile || fi_multifile)
+ && die->die_ck_state != CK_BAD)
+ {
+ value = read_32 (ptr);
+ if (value >= debug_sections[DEBUG_STR].size)
+ die->die_ck_state = CK_BAD;
+ else
+ {
+ unsigned char *p = debug_sections[DEBUG_STR].data + value;
+ unsigned int l = strlen ((char *) p) + 1;
+ 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 (p, l, die->u.p1.die_hash);
+ handled = true;
+ }
+ }
+ else
+ {
+ ptr += 4;
+ if (only_hash_name_p && t->attr[i].attr == DW_AT_name)
+ {
+ s = t->attr[i].attr;
+ die_hash2 = iterative_hash_object (s, die_hash2);
+ die_hash2
+ = iterative_hash (old_ptr, ptr - old_ptr, die_hash2);
+ }
+ }
+ break;
+ case DW_FORM_line_strp:
+ /* There is no .debug_line_str in the alt file, so we cannot
+ move this DIE unless we change the string reference.
+ This is not that bad because DW_FORM_line_strp is often
+ only used in the CU DIE for file name and comp_dir and we
+ don't move the CU DIE anyway. */
+ die->die_ck_state = CK_BAD;
+ break;
+ case DW_FORM_string:
+ ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+ if (only_hash_name_p && t->attr[i].attr == DW_AT_name)
+ {
+ s = t->attr[i].attr;
+ die_hash2 = iterative_hash_object (s, die_hash2);
+ die_hash2
+ = iterative_hash (old_ptr, ptr - old_ptr, die_hash2);
+ }
+ 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 && cu->cu_version < 4)
+ {
+ /* Old DWARF uses blocks instead of exprlocs. */
+ 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_rank:
+ case DW_AT_call_value:
+ case DW_AT_call_target:
+ case DW_AT_call_target_clobbered:
+ case DW_AT_call_data_location:
+ case DW_AT_call_data_value:
+ 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;
+ }
+ }
+ 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; /* Skip expr/blocks. */
+ 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);
+ }
+
+ if (dump_checksum_p)
+ fprintf (stderr, "DIE %x, hash: %x, attr (%d)\n", die->die_offset,
+ die->u.p1.die_hash, i);
+ }
+
+ for (child = die->die_child, i = 0; child; child = child->die_sib, ++i)
+ if (checksum_die (dso, cu,
+ 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);
+ if (dump_checksum_p)
+ fprintf (stderr, "DIE %x, hash: %x, child (%i)\n",
+ die->die_offset, die->u.p1.die_hash, i);
+ die->die_no_multifile
+ |= child->die_no_multifile;
+ }
+ else
+ die->die_ck_state = CK_BAD;
+ }
+ if (die->die_ck_state == CK_BEING_COMPUTED)
+ die->die_ck_state = CK_KNOWN;
+
+ if (dump_checksum_p)
+ fprintf (stderr, "DIE %x, hash: %x, final\n", die->die_offset,
+ die->u.p1.die_hash);
+
+ if (only_hash_name_p)
+ {
+ unsigned int tmp = die->u.p1.die_hash;
+ die->u.p1.die_hash = die_hash2;
+ die->u.p1.die_hash2 = tmp;
+ }
+
+ 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_cu_ref cu, 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);
+ (*second_idx)++;
+ }
+ }
+ 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_addr:
+ if (unlikely (op_multifile || rd_multifile || fi_multifile))
+ i = -2U;
+ break;
+ 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;
+ skip_leb128 (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:
+ if (unlikely (op_multifile || rd_multifile || fi_multifile))
+ {
+ value = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4);
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ assert (t->attr[i].attr != DW_AT_sibling);
+ if (top_die == NULL)
+ break;
+ ref = off_htab_lookup (cu, value);
+ goto finish_ref;
+ }
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_addr:
+ ptr += ptr_size;
+ break;
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ 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:
+ case DW_FORM_line_strp:
+ ptr += 4;
+ break;
+ case DW_FORM_data8:
+ case DW_FORM_ref_sig8:
+ ptr += 8;
+ break;
+ case DW_FORM_data16:
+ ptr += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ skip_leb128 (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 (cu, 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;
+ finish_ref:
+ reft = ref;
+ while (!reft->die_root
+ && 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_root)
+ top_die->die_ck_state = CK_BAD;
+ else
+ {
+ unsigned int r = checksum_ref_die (die_cu (reft), 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;
+ else
+ top_die->die_no_multifile |= reft->die_no_multifile;
+ }
+ 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 (cu,
+ 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;
+ bool no_multifile = 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;
+ else if (arr[i]->die_no_multifile)
+ no_multifile = 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 (no_multifile)
+ arr[i]->die_no_multifile = 1;
+ 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 (checksum_cycle_opt && minidx != -1U)
+ {
+ idx = 0;
+ checksum_ref_die (die_cu (arr[minidx]), 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 DIE. 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 (die_cu (arr[i]), 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. u.p1.die_ref_hash should have u.p1.die_hash
+ iteratively hashed into it already. */
+static hashval_t
+die_hash (const void *p)
+{
+ dw_die_ref die = (dw_die_ref) p;
+
+ return die->u.p1.die_ref_hash;
+}
+
+/* Freelist of !die->die_toplevel DIEs, chained through die_sib fields. */
+static dw_die_ref die_nontoplevel_freelist;
+/* Freelist of die->die_collapsed_child DIEs, chained through die_parent
+ fields. */
+static dw_die_ref die_collapsed_child_freelist;
+
+/* Return pointer after the attributes of a DIE from a cu with CU_VERSION
+ which uses abbrevs T and starts at PTR. */
+static unsigned char *
+skip_attrs_1 (unsigned int cu_version, struct abbrev_tag *t, unsigned char *ptr)
+{
+ unsigned int i;
+ for (i = 0; i < t->nattr; ++i)
+ ptr = skip_attr (cu_version, &t->attr[i], ptr);
+
+ return ptr;
+}
+
+/* Return pointer after the attributes of a DIE from CU which uses abbrevs
+ T and starts at PTR. */
+static unsigned char *
+skip_attrs (dw_cu_ref cu, struct abbrev_tag *t, unsigned char *ptr)
+{
+ return skip_attrs_1 (cu->cu_version, t, ptr);
+}
+
+/* Expand children of TOP_DIE that have been collapsed by
+ collapse_child. CHECKSUM is true if checksum should be
+ computed - expansion is performed during read_debug_info
+ when duplicates are looked for - or false, if the expansion
+ is performed late (e.g. during compute_abbrevs or write_{info,types}. */
+static void
+expand_child (dw_die_ref top_die, bool checksum)
+{
+ dw_cu_ref cu = die_cu (top_die);
+ dw_die_ref *diep = &top_die->die_child;
+ dw_die_ref parent = top_die, child;
+ unsigned char *ptr, *base;
+ struct abbrev_tag tag, *t;
+ dw_die_ref die;
+ unsigned int tick = checksum ? top_die->u.p1.die_enter + 1 : 0;
+
+ if (unlikely (cu->cu_kind == CU_TYPES))
+ base = debug_sections[DEBUG_TYPES].data;
+ else
+ base = debug_sections[DEBUG_INFO].data;
+ ptr = base + top_die->die_offset;
+ if (likely (checksum))
+ ptr += top_die->die_size;
+ else
+ {
+ t = top_die->die_abbrev;
+ skip_leb128 (ptr);
+ ptr = skip_attrs (cu, t, ptr);
+ }
+
+ while (1)
+ {
+ unsigned int die_offset = ptr - base;
+ void **slot;
+ struct dw_die diebuf;
+ dw_die_ref collapsed;
+
+ tag.entry = read_uleb128 (ptr);
+ if (tag.entry == 0)
+ {
+ if (parent == top_die)
+ break;
+ diep = &parent->die_sib;
+ if (checksum)
+ parent->u.p1.die_exit = tick++;
+ parent = parent->die_parent;
+ continue;
+ }
+
+ diebuf.die_offset = die_offset;
+ slot = htab_find_slot_with_hash (cu->cu_kind == CU_TYPES
+ ? types_off_htab : off_htab,
+ &diebuf, off_hash (&diebuf), NO_INSERT);
+ if (slot == NULL)
+ die = NULL;
+ else
+ die = (dw_die_ref) *slot;
+ if (die != NULL && !die->die_collapsed_child)
+ {
+ *diep = die;
+ die->die_parent = parent;
+ die->die_ck_state = CK_UNKNOWN;
+ die->die_ref_seen = 0;
+ assert (!checksum || die->u.p1.die_enter == tick);
+ if (die->die_abbrev->children)
+ {
+ diep = &die->die_child;
+ parent = die;
+ }
+ else
+ {
+ diep = &die->die_sib;
+ assert (!checksum || die->u.p1.die_exit == tick);
+ }
+ tick++;
+ if (checksum)
+ ptr = base + die_offset + die->die_size;
+ else
+ ptr = skip_attrs (cu, die->die_abbrev, ptr);
+ continue;
+ }
+
+ collapsed = die;
+ t = htab_find_with_hash (cu->cu_abbrev, &tag, tag.entry);
+ if (die_nontoplevel_freelist)
+ {
+ die = die_nontoplevel_freelist;
+ die_nontoplevel_freelist = die->die_sib;
+ }
+ else
+ die = pool_alloc (dw_die, offsetof (struct dw_die, die_dup));
+ memset (die, '\0', offsetof (struct dw_die, die_dup));
+ *diep = die;
+ die->die_tag = t->tag;
+ die->die_abbrev = t;
+ die->die_offset = die_offset;
+ die->die_parent = parent;
+ if (checksum)
+ {
+ 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;
+
+ ptr = skip_attrs (cu, t, ptr);
+ die->die_size = (ptr - base) - die_offset;
+ if (collapsed != NULL)
+ {
+ die->die_referenced = collapsed->die_referenced;
+ *slot = (void *) die;
+ memset (collapsed, '\0', offsetof (struct dw_die, die_child));
+ collapsed->die_parent = die_collapsed_child_freelist;
+ die_collapsed_child_freelist = collapsed;
+ }
+ }
+ assert (!checksum || top_die->u.p1.die_exit == tick);
+ top_die->die_collapsed_children = 0;
+ if (checksum && likely (cu->cu_kind != CU_TYPES))
+ for (child = top_die->die_child; child; child = child->die_sib)
+ checksum_die (NULL, cu, top_die, child);
+}
+
+/* Call expand_child on all collapsed toplevel children DIEs. */
+static bool
+expand_children (dw_die_ref die)
+{
+ dw_die_ref child;
+ bool ret = false;
+ for (child = die->die_child; child; child = child->die_sib)
+ if (child->die_named_namespace)
+ ret |= expand_children (child);
+ else if (child->die_collapsed_children)
+ {
+ expand_child (child, false);
+ ret = true;
+ }
+ return ret;
+}
+
+static unsigned odr_phase;
+
+/* 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_cu_ref cu1, dw_cu_ref cu2,
+ 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 only_compare_name_p;
+
+#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
+ || (!odr && (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_root && !die2->die_root);
+
+ if (uni_lang_p && die1 == top_die1 && die2 == top_die2
+ && cu1->lang != cu2->lang)
+ return 0;
+
+ only_compare_name_p
+ = odr && die1->die_odr_state != ODR_NONE && die2->die_odr_state != ODR_NONE;
+
+ if (only_compare_name_p && odr_phase == 1)
+ {
+ const char *name1 = get_AT_string (die1, DW_AT_name);
+ const char *name2 = get_AT_string (die2, DW_AT_name);
+ // TODO: Handle DW_AT_linkage_name?
+ if (name1 == NULL || name2 == NULL)
+ return 0;
+ if (strcmp (name1, name2) != 0)
+ return 0;
+ }
+ else if (die1->u.p1.die_exit - die1->u.p1.die_enter
+ != die2->u.p1.die_exit - die2->u.p1.die_enter)
+ return 0;
+
+ if (only_compare_name_p && odr_phase == 2
+ && die1->die_odr_state == ODR_DEF && die2->die_odr_state == ODR_DEF)
+ {
+ if (die1->u.p1.die_hash2 != die2->u.p1.die_hash2)
+ return 0;
+ }
+
+ t1 = die1->die_abbrev;
+ t2 = die2->die_abbrev;
+ if (likely (!fi_multifile))
+ {
+ ptr1 = debug_sections[DEBUG_INFO].data + die1->die_offset;
+ ptr2 = debug_sections[DEBUG_INFO].data + die2->die_offset;
+ }
+ else
+ {
+ if (cu1->cu_kind == CU_ALT)
+ ptr1 = alt_data[DEBUG_INFO];
+ else
+ ptr1 = debug_sections[DEBUG_INFO].data;
+ ptr1 += die1->die_offset;
+ if (cu2->cu_kind == CU_ALT)
+ ptr2 = alt_data[DEBUG_INFO];
+ else
+ ptr2 = debug_sections[DEBUG_INFO].data;
+ ptr2 += die2->die_offset;
+ }
+ skip_leb128 (ptr1);
+ skip_leb128 (ptr2);
+ i = 0;
+ j = 0;
+ if (die1->die_toplevel)
+ {
+ for (ref1 = die1->die_parent, ref2 = die2->die_parent; ref1 && ref2; )
+ {
+ 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;
+ ref1 = ref1->die_root ? NULL : ref1->die_parent;
+ ref2 = ref2->die_root ? NULL : ref2->die_parent;
+ }
+ if (ref1 == NULL || ref2 == NULL)
+ return 0;
+ /* 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). */
+ if (!die2->die_op_type_referenced)
+ die2->die_remove = 1;
+ obstack_ptr_grow (&ob, die2);
+ if (likely (die2->die_nextdup == NULL))
+ {
+ die2->die_dup = die1;
+ die2->die_nextdup = die1->die_nextdup;
+ obstack_ptr_grow (&ob, NULL);
+ }
+ else
+ {
+ dw_die_ref next;
+ for (next = die2; next->die_nextdup; next = next->die_nextdup)
+ next->die_dup = die1;
+ next->die_dup = die1;
+ next->die_nextdup = die1->die_nextdup;
+ obstack_ptr_grow (&ob, next);
+ }
+ die1->die_nextdup = die2;
+ }
+
+ if (only_compare_name_p && odr_phase == 1)
+ return 1;
+
+ 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: skip_leb128 (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: skip_leb128 (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:
+ case DW_AT_call_return_pc:
+ case DW_AT_call_pc:
+ /* We shouldn't be hitting DIEs with attributes referencing
+ addresses and we should have removed DW_AT_subling. */
+ 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;
+ case DW_FORM_sdata: value1 = read_sleb128 (ptr1); break;
+ case DW_FORM_implicit_const: value1 = t1->values[i]; 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;
+ case DW_FORM_sdata: value2 = read_sleb128 (ptr2); break;
+ case DW_FORM_implicit_const: value2 = t2->values[j]; break;
+ default: abort ();
+ }
+ if (ignore_locus)
+ {
+ i++;
+ j++;
+ continue;
+ }
+ if ((value1 == 0) ^ (value2 == 0))
+ FAIL;
+ if (value1 != 0)
+ {
+ struct dw_file *cu_file1
+ = &cu1->cu_files[value1 - 1];
+ struct dw_file *cu_file2
+ = &cu2->cu_files[value2 - 1];
+
+ if (cu_file1->time != cu_file2->time
+ || cu_file1->size != cu_file2->size
+ || strcmp (cu_file1->file, cu_file2->file))
+ FAIL;
+
+ 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_angle_brackets_encapsulated_no_slash
+ && 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 (cu1->cu_comp_dir != NULL)
+ {
+ if (cu2->cu_comp_dir == NULL
+ || strcmp (cu1->cu_comp_dir, cu2->cu_comp_dir))
+ FAIL;
+ }
+ else if (cu2->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;
+ }
+ 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;
+ case DW_FORM_sdata: value1 = read_sleb128 (ptr1); break;
+ case DW_FORM_implicit_const: value1 = t1->values[i]; 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;
+ case DW_FORM_sdata: value2 = read_sleb128 (ptr2); break;
+ case DW_FORM_implicit_const: value2 = t2->values[j]; break;
+ default: abort ();
+ }
+ if (value1 != value2)
+ FAIL;
+ i++;
+ j++;
+ continue;
+ default:
+ break;
+ }
+
+ switch (form1)
+ {
+ case DW_FORM_ref_addr:
+ if (likely (!op_multifile && !rd_multifile && !fi_multifile))
+ {
+ if (form1 != form2)
+ FAIL;
+ break;
+ }
+ /* FALLTHRU */
+ case DW_FORM_ref_udata:
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ switch (form2)
+ {
+ case DW_FORM_ref_addr:
+ if (likely (!op_multifile && !rd_multifile && !fi_multifile))
+ FAIL;
+ break;
+ case DW_FORM_ref_udata:
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ break;
+ default:
+ FAIL;
+ }
+ break;
+ default:
+ if (form1 != form2)
+ FAIL;
+ break;
+ }
+
+ switch (form1)
+ {
+ case DW_FORM_addr:
+ ptr1 += ptr_size;
+ ptr2 += ptr_size;
+ break;
+ case DW_FORM_flag_present:
+ break;
+ case DW_FORM_implicit_const:
+ if ((!ignore_locus || old_ptr1) && t1->values[i] != t2->values[j])
+ FAIL;
+ 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_data16:
+ ptr1 += 16;
+ ptr2 += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ skip_leb128 (ptr1);
+ skip_leb128 (ptr2);
+ break;
+ case DW_FORM_strp:
+ if (unlikely (op_multifile || rd_multifile || fi_multifile))
+ {
+ value1 = read_32 (ptr1);
+ value2 = read_32 (ptr2);
+ if (fi_multifile)
+ {
+ if (strcmp ((char *) (cu1->cu_kind == CU_ALT
+ ? alt_data[DEBUG_STR]
+ : debug_sections[DEBUG_STR].data)
+ + value1,
+ (char *) (cu2->cu_kind == CU_ALT
+ ? alt_data[DEBUG_STR]
+ : debug_sections[DEBUG_STR].data)
+ + value2) != 0)
+ FAIL;
+ i++;
+ j++;
+ continue;
+ }
+ if (strcmp ((char *) debug_sections[DEBUG_STR].data + value1,
+ (char *) debug_sections[DEBUG_STR].data + value2)
+ != 0)
+ FAIL;
+ i++;
+ j++;
+ continue;
+ }
+ ptr1 += 4;
+ ptr2 += 4;
+ break;
+ case DW_FORM_line_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_addr:
+ if (likely (!op_multifile && !rd_multifile && !fi_multifile))
+ {
+ ptr1 += cu1->cu_version == 2 ? ptr_size : 4;
+ ptr2 += cu2->cu_version == 2 ? ptr_size : 4;
+ break;
+ }
+ /* FALLTHRU */
+ 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_addr:
+ value1 = read_size (ptr1, cu1->cu_version == 2
+ ? ptr_size : 4)
+ - cu1->cu_offset;
+ ptr1 += cu1->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_ref_udata:
+ value1 = read_uleb128 (ptr1);
+ break;
+ case DW_FORM_ref1:
+ value1 = read_8 (ptr1);
+ break;
+ case DW_FORM_ref2:
+ value1 = read_16 (ptr1);
+ break;
+ case DW_FORM_ref4:
+ value1 = read_32 (ptr1);
+ break;
+ case DW_FORM_ref8:
+ value1 = read_64 (ptr1);
+ break;
+ default: abort ();
+ }
+ ref1 = off_htab_lookup (cu1, cu1->cu_offset + value1);
+ switch (form2)
+ {
+ case DW_FORM_ref_addr:
+ value2 = read_size (ptr2, cu2->cu_version == 2
+ ? ptr_size : 4)
+ - cu2->cu_offset;
+ ptr2 += cu2->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_ref_udata:
+ value2 = read_uleb128 (ptr2);
+ break;
+ case DW_FORM_ref1:
+ value2 = read_8 (ptr2);
+ break;
+ case DW_FORM_ref2:
+ value2 = read_16 (ptr2);
+ break;
+ case DW_FORM_ref4:
+ value2 = read_32 (ptr2);
+ break;
+ case DW_FORM_ref8:
+ value2 = read_64 (ptr2);
+ break;
+ default: abort ();
+ }
+ ref2 = off_htab_lookup (cu2, cu2->cu_offset + value2);
+ assert (ref1 != NULL && ref2 != NULL);
+ if (unlikely (op_multifile || low_mem))
+ {
+ if (die1->die_collapsed_children && ref1->die_collapsed_child)
+ {
+ expand_child (die1, true);
+ ref1 = off_htab_lookup (cu1, cu1->cu_offset + value1);
+ }
+ assert (ref2->die_collapsed_child == 0);
+ }
+ if (likely (!ref1->die_collapsed_child)
+ && die_cu (ref1) == cu1
+ && 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 (die_cu (ref2) != cu2
+ || 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;
+ dw_cu_ref refcu1, refcu2;
+ while (reft1->die_toplevel == 0)
+ reft1 = reft1->die_parent;
+ while (reft2->die_toplevel == 0)
+ reft2 = reft2->die_parent;
+ if (unlikely (ref1->die_collapsed_child))
+ {
+ if (ref1->die_tag
+ != ref2->u.p1.die_enter - reft2->u.p1.die_enter)
+ FAIL;
+ }
+ else if (ref1->u.p1.die_enter - reft1->u.p1.die_enter
+ != ref2->u.p1.die_enter - reft2->u.p1.die_enter)
+ FAIL;
+ refcu1 = die_cu (reft1);
+ refcu2 = die_cu (reft2);
+ if (unlikely (refcu1->cu_chunk == refcu2->cu_chunk)
+ && likely (!fi_multifile))
+ {
+ if (reft1->die_dup
+ && die_cu (reft1->die_dup)->cu_chunk
+ == refcu1->cu_chunk)
+ reft1 = reft1->die_dup;
+ if (reft2->die_dup
+ && die_cu (reft2->die_dup)->cu_chunk
+ == refcu2->cu_chunk)
+ reft2 = reft2->die_dup;
+ if (reft2->die_offset < reft1->die_offset)
+ {
+ dw_die_ref tem = reft1;
+ reft1 = reft2;
+ reft2 = tem;
+ }
+ if (reft1->die_dup == NULL && reft2->die_dup != NULL)
+ {
+ dw_die_ref tem = reft1;
+ reft1 = reft2;
+ reft2 = tem;
+ }
+ }
+ /* 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 (reft1->die_dup)
+ reft1 = reft1->die_dup;
+ refcu1 = die_cu (reft1);
+ refcu2 = die_cu (reft2);
+ if (die_eq_1 (refcu1, refcu2, 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++;
+ }
+
+ if (unlikely (op_multifile || low_mem))
+ {
+ if (die1->die_collapsed_children)
+ expand_child (die1, true);
+ assert (die2->die_collapsed_children == 0);
+ }
+
+ for (child1 = die1->die_child, child2 = die2->die_child;
+ child1 && child2;
+ child1 = child1->die_sib, child2 = child2->die_sib)
+ if (die_eq_1 (cu1, cu2, top_die1, top_die2, child1, child2) == 0)
+ FAIL;
+
+ if (child1 || child2)
+ {
+ fail:
+ return 0;
+ }
+
+ if (unlikely (fi_multifile))
+ assert (cu1->cu_kind == CU_ALT && cu2->cu_kind != CU_ALT);
+ 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;
+ ret = die_eq_1 (die_cu (die1), die_cu (die2), die1, die2, die1, die2);
+ count = obstack_object_size (&ob) / sizeof (void *);
+ arr = (dw_die_ref *) obstack_finish (&ob);
+ if (!ret)
+ for (i = count; i;)
+ {
+ dw_die_ref die;
+ i -= 2;
+ die = arr[i]->die_dup;
+ if (likely (arr[i + 1] == NULL))
+ {
+ die->die_nextdup = arr[i]->die_nextdup;
+ arr[i]->die_nextdup = NULL;
+ arr[i]->die_dup = NULL;
+ }
+ else
+ {
+ dw_die_ref next;
+
+ assert (die->die_nextdup == arr[i]);
+ for (next = arr[i]->die_nextdup;
+ next != arr[i + 1];
+ next = next->die_nextdup)
+ {
+ assert (next->die_dup == die);
+ next->die_dup = arr[i];
+ }
+ assert (next->die_dup == die);
+ next->die_dup = arr[i];
+ die->die_nextdup = next->die_nextdup;
+ next->die_nextdup = NULL;
+ arr[i]->die_dup = 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;
+
+/* After read_multifile dup_htab is moved to this variable. */
+static htab_t alt_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 dw_cu_ref first_cu, last_cu;
+
+/* After read_multifile first_cu is copied to this variable. */
+static dw_cu_ref alt_first_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 (wr_multifile ? die->die_no_multifile : die->die_remove)
+ return 0;
+ for (child = die->die_child; child; child = child->die_sib)
+ ret += calc_sizes (child);
+ return ret;
+}
+
+/* Verify the duplicate chains starting at DIE. If ORDERED, also check that
+ the duplicate chain is in the correct order. */
+static void
+verify_dups (dw_die_ref die, bool ordered)
+{
+ dw_die_ref d, prev;
+
+ assert (die->die_dup == NULL);
+ assert (die->die_collapsed_children == 0);
+ assert (die->die_remove == 0);
+
+ for (prev = die, d = prev->die_nextdup;
+ d;
+ prev = d, d = prev->die_nextdup)
+ {
+ if (ordered)
+ assert (die_cu (prev)->cu_chunk <= die_cu (d)->cu_chunk);
+ assert (d->die_offset != -1U);
+ assert (d->die_dup == die);
+ assert (d->die_remove != d->die_op_type_referenced);
+ assert (d->die_tag == die->die_tag);
+ }
+}
+
+/* Walk toplevel DIEs in tree rooted by PARENT, and see if they
+ match previously processed DIEs. */
+static int
+find_dups (dw_die_ref parent)
+{
+ void **slot;
+ dw_die_ref child;
+
+ if (stats_p && parent->die_root)
+ stats->root_cnt++;
+
+ for (child = parent->die_child; child; child = child->die_sib)
+ {
+ if (child->die_ck_state == CK_KNOWN)
+ {
+ if (stats_p)
+ stats->lower_toplevel_with_checksum++;
+ if (child->die_dup != NULL)
+ continue;
+ slot = htab_find_slot_with_hash (dup_htab, child,
+ child->u.p1.die_ref_hash,
+ INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot == NULL)
+ *slot = child;
+ }
+ else if (child->die_named_namespace)
+ {
+ if (stats_p)
+ stats->namespace_cnt++;
+ if (find_dups (child))
+ return 1;
+ }
+ else if (stats_p)
+ stats->lower_toplevel++;
+ }
+ return 0;
+}
+
+/* Like find_dups, but for the last multifile optimization phase,
+ where it only looks at duplicates in the common .debug_info
+ section. */
+static int
+find_dups_fi (dw_die_ref parent)
+{
+ dw_die_ref child;
+
+ for (child = parent->die_child; child; child = child->die_sib)
+ {
+ if (child->die_ck_state == CK_KNOWN)
+ {
+ if (child->die_dup != NULL)
+ continue;
+ htab_find_with_hash (alt_dup_htab, child, child->u.p1.die_ref_hash);
+ }
+ else if (child->die_named_namespace)
+ if (find_dups_fi (child))
+ return 1;
+ }
+ return 0;
+}
+
+/* Return value of DW_AT_name for DIE, or for its specification or abstract
+ origin. */
+static const char *
+get_name (dw_die_ref die)
+{
+ if (die->die_collapsed_child)
+ return NULL;
+ const char *name = get_AT_string (die, DW_AT_name);
+ if (name)
+ return name;
+ dw_cu_ref cu = die_cu (die);
+ bool present;
+ enum dwarf_form form;
+ unsigned int value = get_AT_int (die, DW_AT_specification, &present, &form);
+ if (present)
+ {
+ dw_die_ref ref;
+ if (form != DW_FORM_ref_addr)
+ value = cu->cu_offset + value;
+ ref = off_htab_lookup (cu, value);
+ if (ref)
+ return get_name (ref);
+ }
+ value = get_AT_int (die, DW_AT_abstract_origin, &present, &form);
+ if (present)
+ {
+ dw_die_ref ref;
+ if (form != DW_FORM_ref_addr)
+ value = cu->cu_offset + value;
+ ref = off_htab_lookup (die_cu (die), value);
+ if (ref)
+ return get_name (ref);
+ }
+ return NULL;
+}
+
+/* Dump type of DIE to stderr. */
+static void
+dump_type (dw_die_ref die)
+{
+ bool present;
+ enum dwarf_form form;
+ if (die->die_collapsed_child)
+ return;
+ unsigned int value = get_AT_int (die, DW_AT_type, &present, &form);
+ if (!present)
+ return;
+
+ dw_cu_ref cu = die_cu (die);
+ if (cu == NULL)
+ return;
+
+ dw_die_ref ref;
+ if (form != DW_FORM_ref_addr)
+ value = cu->cu_offset + value;
+ fprintf (stderr, " (type: %x", value);
+ ref = off_htab_lookup (cu, value);
+ if (ref != NULL && !ref->die_collapsed_child)
+ {
+ const char *type_name = get_name (ref);
+ if (type_name)
+ fprintf (stderr, " %s", type_name);
+ fprintf (stderr, " %s", get_DW_TAG_name (ref->die_tag) + 7);
+ dump_type (ref);
+ }
+ fprintf (stderr, ")");
+}
+
+/* Dump DIE to stderr with INDENT. */
+static void
+dump_die_with_indent (int indent, dw_die_ref die)
+{
+ if (die == NULL)
+ fprintf (stderr, "%*s null", indent, "");
+ else if (die->die_offset == -1U)
+ {
+ fprintf (stderr, "%*s -1 %s", indent, "",
+ get_DW_TAG_name (die->die_tag) + 7);
+ dw_die_ref d = die->die_nextdup;
+ while (d)
+ {
+ const char *name = get_name (d);
+ fprintf (stderr, " -> %x %s %s", d->die_offset, name ? name : "",
+ get_DW_TAG_name (d->die_tag) + 7);
+ d = d->die_nextdup;
+ }
+ }
+ else if (die->die_collapsed_child)
+ {
+ fprintf (stderr, "%*s %x %c", indent, "", die->die_offset,
+ die->die_ck_state == CK_KNOWN ? 'O' : 'X');
+ }
+ else
+ {
+ const char *name = get_name (die);
+ fprintf (stderr, "%*s %x %c %x", indent, "", die->die_offset,
+ die->die_ck_state == CK_KNOWN ? 'O' : 'X',
+ (unsigned) die->u.p1.die_hash);
+ if (odr && die->die_odr_state != ODR_NONE
+ && die->die_odr_state != ODR_UNKNOWN)
+ fprintf (stderr, "(%x)", (unsigned) die->u.p1.die_hash2);
+ fprintf (stderr, " %x %s %s", (unsigned) die->u.p1.die_ref_hash,
+ name ? name : "", get_DW_TAG_name (die->die_tag) + 7);
+ dump_type (die);
+ }
+ fprintf (stderr, "\n");
+}
+
+/* Dump DIE to stderr. */
+static void
+dump_die (dw_die_ref die)
+{
+ dump_die_with_indent (0, die);
+}
+
+static void
+dump_dups (dw_die_ref die)
+{
+ dw_die_ref d;
+ for (d = die; d; d = d->die_nextdup)
+ dump_die (d);
+}
+
+/* Dump DIE tree at tree depth DEPTH. */
+static void
+dump_dies (int depth, dw_die_ref die)
+{
+ dw_die_ref child;
+ dump_die_with_indent (depth, die);
+ for (child = die->die_child; child; child = child->die_sib)
+ dump_dies (depth + 1, child);
+}
+
+/* Hash table for .debug_str. Depending on multifile optimization
+ phase this hash table has 3 different hash/equality functions.
+ The first set is used to record tail optimized strings, during
+ write_multifile the whole .debug_str section is written as is,
+ plus then all the strings which are just suffixes of other
+ strings. E.g. if .debug_str section contains "foobar" string
+ and .debug_info section refers to the whole "foobar" string
+ as well as "bar" by referring to "foobar" + 3.
+ The second set is used during op_multifile and fi_multifile,
+ noting each string and in addition to that how many times it
+ has been seen (0, 1 or more than 1). If 0 then it isn't present
+ in the hash table, 1 has lowest bit of new_off clear, more than 1
+ the LSB of new_off is set.
+ The final set is used during finalize_strp and afterwards, it is
+ then used to map strings to their location in the new .debug_str
+ section. */
+static htab_t strp_htab;
+/* Current offset in .debug_str when adding the tail optimized strings.
+ This is initially the size of .debug_str section in the object,
+ and as unique tail optimized strings are found, this is increased
+ each time. */
+static unsigned int max_strp_off;
+
+/* At the end of read_multifile strp_htab is moved to this variable,
+ which is used to find strings in the shared .debug_str section. */
+static htab_t alt_strp_htab;
+
+/* Structure describing strings in strp_htab. */
+struct strp_entry
+{
+ /* Original .debug_str offset. */
+ unsigned int off;
+ /* New .debug_str offset, or when using strp_{hash,eq}2
+ this is initially iterative hash of the string with the
+ LSB bit used for whether the string has been seen just once
+ or more than once. */
+ unsigned int new_off;
+};
+ALIGN_STRUCT (strp_entry)
+
+/* Hash function in strp_htab used for discovery of tail optimized
+ strings. */
+static hashval_t
+strp_hash (const void *p)
+{
+ struct strp_entry *s = (struct strp_entry *)p;
+
+ return s->off;
+}
+
+/* Corresponding equality function in strp_htab. */
+static int
+strp_eq (const void *p, const void *q)
+{
+ struct strp_entry *s1 = (struct strp_entry *)p;
+ struct strp_entry *s2 = (struct strp_entry *)q;
+
+ return s1->off == s2->off;
+}
+
+/* Hash function in strp_htab used to find what strings are
+ used by more than one object. */
+static hashval_t
+strp_hash2 (const void *p)
+{
+ struct strp_entry *s = (struct strp_entry *)p;
+
+ return s->new_off & ~1U;
+}
+
+/* Corresponding equality function in strp_htab. */
+static int
+strp_eq2 (const void *p, const void *q)
+{
+ struct strp_entry *s1 = (struct strp_entry *)p;
+ struct strp_entry *s2 = (struct strp_entry *)q;
+
+ return strcmp ((char *) debug_sections[DEBUG_STR].data + s1->off,
+ (char *) debug_sections[DEBUG_STR].data + s2->off) == 0;
+}
+
+/* Hash function in strp_htab used from finalize_strp onwards,
+ mapping strings into strings in the new .debug_str section. */
+static hashval_t
+strp_hash3 (const void *p)
+{
+ return hash (p, strlen (p));
+}
+
+/* Corresponding equality function in strp_htab. */
+static int
+strp_eq3 (const void *p, const void *q)
+{
+ return strcmp (p, q) == 0;
+}
+
+/* Called for each DW_FORM_strp offset seen during initial
+ .debug_{info,types,macro} parsing. Before fi_multifile phase
+ this records just tail optimized strings, during fi_multifile
+ it checks whether the string is already in the shared .debug_str
+ section and if not, notes that it will need to be added to the
+ new local .debug_str section. */
+static void
+note_strp_offset (unsigned int off)
+{
+ void **slot;
+ struct strp_entry se;
+
+ if (unlikely (fi_multifile))
+ {
+ unsigned char *p;
+ unsigned int len;
+ hashval_t hash;
+
+ p = debug_sections[DEBUG_STR].data + off;
+ len = strlen ((char *) p);
+ hash = hash (p, len);
+ if (alt_strp_htab)
+ {
+ if (htab_find_with_hash (alt_strp_htab, p, hash))
+ return;
+ }
+ if (strp_htab == NULL)
+ {
+ unsigned int strp_count = debug_sections[DEBUG_STR].size / 64;
+
+ if (strp_count < 100)
+ strp_count = 100;
+ strp_htab = htab_try_create (strp_count, strp_hash2, strp_eq2, NULL);
+ if (strp_htab == NULL)
+ dwz_oom ();
+ }
+
+ se.off = off;
+ se.new_off = hash | 1;
+ slot = htab_find_slot_with_hash (strp_htab, &se, se.new_off & ~1U, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot == NULL)
+ {
+ struct strp_entry *s = pool_alloc (strp_entry, sizeof (*s));
+ *s = se;
+ *slot = (void *) s;
+ }
+ return;
+ }
+ if (off >= debug_sections[DEBUG_STR].size || off == 0)
+ return;
+ if (debug_sections[DEBUG_STR].data[off - 1] == '\0')
+ return;
+ if (strp_htab == NULL)
+ {
+ if (multifile == NULL)
+ return;
+ strp_htab = htab_try_create (50, strp_hash, strp_eq, NULL);
+ if (strp_htab == NULL)
+ dwz_oom ();
+ max_strp_off = debug_sections[DEBUG_STR].size;
+ }
+ se.off = off;
+ slot = htab_find_slot_with_hash (strp_htab, &se, off, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot == NULL)
+ {
+ struct strp_entry *s = pool_alloc (strp_entry, sizeof (*s));
+ s->off = off;
+ s->new_off = max_strp_off;
+ max_strp_off += strlen ((char *) debug_sections[DEBUG_STR].data
+ + off) + 1;
+ if (max_strp_off < s->new_off)
+ {
+ htab_delete (strp_htab);
+ strp_htab = NULL;
+ max_strp_off = 0;
+ multifile = NULL;
+ error (0, 0, ".debug_str too large for multi-file optimization");
+ }
+ *slot = (void *) s;
+ }
+}
+
+/* Map offset in original .debug_str section into
+ offset in new .debug_str, either the shared .debug_str
+ or new local .debug_str. */
+static unsigned
+lookup_strp_offset (unsigned int off)
+{
+ struct strp_entry *s, se;
+
+ if (unlikely (op_multifile || fi_multifile))
+ {
+ unsigned char *p;
+ unsigned int len;
+ hashval_t hash;
+
+ p = debug_sections[DEBUG_STR].data + off;
+ len = strlen ((char *) p);
+ hash = hash (p, len);
+ if (alt_strp_htab)
+ {
+ unsigned char *q = (unsigned char *)
+ htab_find_with_hash (alt_strp_htab, p, hash);
+ if (q != NULL)
+ return q - alt_data[DEBUG_STR];
+ }
+ assert (strp_htab);
+ p = (unsigned char *) htab_find_with_hash (strp_htab, p, hash);
+ assert (p != NULL);
+ return p - debug_sections[DEBUG_STR].new_data;
+ }
+ if (off >= debug_sections[DEBUG_STR].size || off == 0)
+ return off + multi_str_off;
+ if (debug_sections[DEBUG_STR].data[off - 1] == '\0')
+ return off + multi_str_off;
+ se.off = off;
+ s = (struct strp_entry *) htab_find_with_hash (strp_htab, &se, off);
+ return s->new_off + multi_str_off;
+}
+
+/* Note .debug_str offset during write_macro or compute_abbrevs,
+ return either DW_FORM_strp if the string will be in the local
+ .debug_str section, or DW_FORM_strp_sup / DW_FORM_GNU_strp_alt if it
+ will be in the shared .debug_str section. */
+static enum dwarf_form
+note_strp_offset2 (unsigned int off)
+{
+ hashval_t hash;
+ struct strp_entry se;
+ unsigned char *p, *q;
+
+ if (likely (fi_multifile))
+ {
+ unsigned int len;
+
+ if (alt_strp_htab)
+ {
+ p = debug_sections[DEBUG_STR].data + off;
+ len = strlen ((char *) p);
+ hash = hash (p, len);
+ if (htab_find_with_hash (alt_strp_htab, p, hash))
+ return dwarf_5 ? DW_FORM_strp_sup : DW_FORM_GNU_strp_alt;
+ }
+ return DW_FORM_strp;
+ }
+ if (off >= debug_sections[DEBUG_STR].size)
+ return DW_FORM_strp;
+ p = debug_sections[DEBUG_STR].data + off;
+ q = (unsigned char *) strchr ((char *) p, '\0');
+ hash = hash (p, q - p);
+ se.off = off;
+ se.new_off = hash & ~1U;
+ struct strp_entry *s = (struct strp_entry *)
+ htab_find_with_hash (strp_htab, &se, se.new_off);
+ assert (s != NULL);
+ s->new_off |= 1;
+ return DW_FORM_strp;
+}
+
+/* Helper to record all strp_entry entries from strp_htab.
+ Called through htab_traverse. */
+static int
+list_strp_entries (void **slot, void *data)
+{
+ struct strp_entry ***end = (struct strp_entry ***) data;
+ **end = (struct strp_entry *) *slot;
+ (*end)++;
+ return 1;
+}
+
+/* Adapted from bfd/merge.c strrevcmp. */
+static int
+strrevcmp (const void *p, const void *q)
+{
+ struct strp_entry *s1 = *(struct strp_entry **)p;
+ struct strp_entry *s2 = *(struct strp_entry **)q;
+ unsigned int len1 = s1->new_off & ~1U;
+ unsigned int len2 = s2->new_off & ~1U;
+ unsigned int len;
+ unsigned char *p1 = debug_sections[DEBUG_STR].data + s1->off;
+ unsigned char *p2 = debug_sections[DEBUG_STR].data + s2->off;
+
+ if (p1[len1])
+ len1++;
+ if (p2[len2])
+ len2++;
+ p1 += len1;
+ p2 += len2;
+ len = len1;
+ if (len2 < len)
+ len = len2;
+ while (len)
+ {
+ p1--;
+ p2--;
+ if (*p1 != *p2)
+ {
+ if (*p1 < *p2)
+ return -1;
+ return 1;
+ }
+ len--;
+ }
+ if (len1 < len2)
+ return 1;
+ if (len1 > len2)
+ return -1;
+ assert (s1->off == s2->off);
+ return 0;
+}
+
+/* Compute new .debug_str section, from strp_htab content,
+ replace strp_htab hash table with a new one, which maps strings
+ to new .debug_str locations. */
+static unsigned int *
+finalize_strp (bool build_tail_offset_list)
+{
+ unsigned int count, new_count, i, *tail_offset_list = NULL;
+ unsigned int strp_index = 0, tail_offset_list_count = 0, k;
+ struct strp_entry **arr, **end;
+ unsigned char *p;
+
+ if (strp_htab == NULL)
+ {
+ debug_sections[DEBUG_STR].new_data = NULL;
+ debug_sections[DEBUG_STR].new_size = 0;
+ return NULL;
+ }
+ count = htab_elements (strp_htab);
+ arr = (struct strp_entry **)
+ obstack_alloc (&ob, count * sizeof (struct strp_entry *));
+ end = arr;
+ htab_traverse (strp_htab, list_strp_entries, (void *) &end);
+ for (i = 0; i < count; i++)
+ {
+ unsigned int len = strlen ((char *) debug_sections[DEBUG_STR].data
+ + arr[i]->off);
+ arr[i]->new_off = (len & ~1U) | (arr[i]->new_off & 1);
+ }
+ qsort (arr, count, sizeof (struct strp_entry *), strrevcmp);
+ htab_delete (strp_htab);
+ strp_htab = NULL;
+ new_count = count;
+ for (i = 0; i < count; i++)
+ if ((arr[i]->new_off & 1) == 0)
+ {
+ arr[i]->off = -1U;
+ arr[i]->new_off = -1U;
+ new_count--;
+ }
+ else
+ {
+ unsigned int len1, len2, lastlen, j;
+ unsigned char *p1, *p2;
+ len1 = arr[i]->new_off & ~1U;
+ p1 = debug_sections[DEBUG_STR].data + arr[i]->off;
+ if (p1[len1])
+ len1++;
+ lastlen = len1;
+ arr[i]->new_off = strp_index;
+ strp_index += len1 + 1;
+ for (j = i + 1; j < count; j++)
+ {
+ len2 = arr[j]->new_off & ~1U;
+ p2 = debug_sections[DEBUG_STR].data + arr[j]->off;
+ if (p2[len2])
+ len2++;
+ if (len2 >= lastlen)
+ break;
+ if (memcmp (p1 + len1 - len2, p2, len2 + 1) != 0)
+ break;
+ arr[j]->new_off = arr[i]->new_off + len1 - len2;
+ lastlen = len2;
+ tail_offset_list_count++;
+ }
+ i = j - 1;
+ }
+ debug_sections[DEBUG_STR].new_data = malloc (strp_index);
+ if (debug_sections[DEBUG_STR].new_data == NULL)
+ dwz_oom ();
+ debug_sections[DEBUG_STR].new_size = strp_index;
+ strp_htab = htab_try_create (new_count < 32 ? 32 : new_count,
+ strp_hash3, strp_eq3, NULL);
+ if (strp_htab == NULL)
+ dwz_oom ();
+ if (build_tail_offset_list && tail_offset_list_count++ != 0)
+ {
+ tail_offset_list
+ = mmap (NULL, tail_offset_list_count * sizeof (int),
+ PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (tail_offset_list == MAP_FAILED)
+ dwz_oom ();
+ }
+ for (i = 0, k = 0, p = debug_sections[DEBUG_STR].new_data; i < count; i++)
+ if (arr[i]->off == -1U && arr[i]->new_off == -1U)
+ continue;
+ else
+ {
+ unsigned int len = strlen ((char *) debug_sections[DEBUG_STR].data
+ + arr[i]->off) + 1;
+ unsigned int j;
+ void **slot;
+
+ memcpy (p, debug_sections[DEBUG_STR].data + arr[i]->off, len);
+ slot = htab_find_slot_with_hash (strp_htab, p,
+ hash (p, len - 1),
+ INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ assert (*slot == NULL);
+ *slot = (void *) p;
+ for (j = i + 1; j < count; j++)
+ if (arr[j]->new_off >= arr[i]->new_off + len)
+ break;
+ else
+ {
+ unsigned char *q = p + arr[j]->new_off - arr[i]->new_off;
+ unsigned int l = len + arr[i]->new_off - arr[j]->new_off;
+ if (tail_offset_list != NULL)
+ tail_offset_list[k++] = arr[j]->new_off;
+ slot = htab_find_slot_with_hash (strp_htab, q,
+ hash (q, l - 1),
+ INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ assert (*slot == NULL);
+ *slot = (void *) q;
+ }
+ p += len;
+ i = j - 1;
+ }
+ assert (p == debug_sections[DEBUG_STR].new_data + strp_index);
+ if (tail_offset_list != NULL)
+ {
+ tail_offset_list[k++] = 0;
+ assert (k == tail_offset_list_count);
+ }
+ obstack_free (&ob, (void *) arr);
+ return tail_offset_list;
+}
+
+enum mark_refs_mode
+{
+ MARK_REFS_FOLLOW_DUPS = 1,
+ MARK_REFS_RETURN_VAL = 2,
+ MARK_REFS_REFERENCED = 4
+};
+
+/* Mark all DIEs referenced from DIE by setting die_ref_seen to 1,
+ unless already marked. */
+static bool
+mark_refs (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, int mode)
+{
+ struct abbrev_tag *t;
+ unsigned int i;
+ unsigned char *ptr;
+ dw_die_ref child;
+
+ 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_addr:
+ if (unlikely (op_multifile))
+ i = -2U;
+ break;
+ 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)
+ {
+ if (unlikely (cu->cu_kind == CU_TYPES))
+ ptr = debug_sections[DEBUG_TYPES].data;
+ else
+ ptr = debug_sections[DEBUG_INFO].data;
+ ptr += die->die_offset;
+ skip_leb128 (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:
+ if (unlikely (op_multifile))
+ {
+ value = read_size (ptr, cu->cu_version == 2
+ ? ptr_size : 4);
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ assert (t->attr[i].attr != DW_AT_sibling);
+ ref = off_htab_lookup (cu, value);
+ if ((mode & MARK_REFS_REFERENCED) != 0)
+ ref->die_referenced = 1;
+ goto finish_ref;
+ }
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_addr:
+ ptr += ptr_size;
+ break;
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ 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:
+ case DW_FORM_line_strp:
+ ptr += 4;
+ break;
+ case DW_FORM_data8:
+ case DW_FORM_ref_sig8:
+ ptr += 8;
+ break;
+ case DW_FORM_data16:
+ ptr += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ skip_leb128 (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)
+ break;
+ ref = off_htab_lookup (cu, cu->cu_offset + value);
+ if ((mode & MARK_REFS_REFERENCED) != 0)
+ ref->die_referenced = 1;
+ if (!ref->die_collapsed_child
+ && ref->u.p1.die_enter >= top_die->u.p1.die_enter
+ && ref->u.p1.die_exit <= top_die->u.p1.die_exit)
+ break;
+ finish_ref:
+ reft = ref;
+ while (!reft->die_root
+ && 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 ((mode & MARK_REFS_FOLLOW_DUPS) && reft->die_dup != NULL)
+ {
+ reft = reft->die_dup;
+ if (die_cu (reft)->cu_kind == CU_PU)
+ break;
+ }
+ if (reft->die_ref_seen == 0)
+ {
+ if ((mode & MARK_REFS_RETURN_VAL))
+ return false;
+ reft->die_ref_seen = 1;
+ mark_refs (die_cu (reft), reft, reft, mode);
+ }
+ 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;
+ }
+ }
+
+ for (child = die->die_child; child; child = child->die_sib)
+ if (!mark_refs (cu, top_die, child, mode))
+ return false;
+ return true;
+}
+
+/* Remove completely unneeded children of DIE and remove unreferenced DIEs
+ from offset hash tables. */
+static void
+remove_dies (dw_cu_ref cu, dw_die_ref die, bool remove)
+{
+ dw_die_ref child, next;
+ if (die->die_toplevel && die->die_ref_seen == 0 && !low_mem)
+ remove = true;
+ for (child = die->die_child; child; child = next)
+ {
+ next = child->die_sib;
+ remove_dies (cu, child, remove);
+ }
+ if (die->die_referenced == 0)
+ {
+ htab_t h = cu->cu_kind == CU_TYPES ? types_off_htab : off_htab;
+ void **slot = htab_find_slot_with_hash (h, die, off_hash (die),
+ NO_INSERT);
+ if (slot != NULL)
+ htab_clear_slot (h, slot);
+ }
+ if (!remove)
+ return;
+ if (die->die_toplevel == 0)
+ {
+ memset (die, '\0', offsetof (struct dw_die, die_dup));
+ die->die_sib = die_nontoplevel_freelist;
+ die_nontoplevel_freelist = die;
+ }
+ else
+ die->die_child = NULL;
+}
+
+/* Remove unneeded children DIEs. During phase 0 die_ref_seen
+ of toplevel DIEs is computed, during phase 1 mark_refs is called
+ to find referenced DIEs, during phase 2 unneeded children DIEs
+ are removed. */
+static void
+remove_unneeded (dw_cu_ref cu, dw_die_ref die, unsigned int phase)
+{
+ dw_die_ref child;
+ for (child = die->die_child; child; child = child->die_sib)
+ {
+ if (child->die_named_namespace)
+ {
+ remove_unneeded (cu, child, phase);
+ if (phase == 2)
+ child->die_ref_seen = 0;
+ }
+ else
+ switch (phase)
+ {
+ case 0:
+ child->die_ref_seen = child->die_dup == NULL;
+ break;
+ case 1:
+ if (child->die_dup == NULL || low_mem)
+ mark_refs (cu, child, child, MARK_REFS_REFERENCED);
+ break;
+ case 2:
+ remove_dies (cu, child, false);
+ child->die_ref_seen = 0;
+ break;
+ }
+ }
+}
+
+/* Entries in meta_abbrev_htab, mapping .debug_abbrev section offsets
+ to abbrev hash tables. */
+struct meta_abbrev_entry
+{
+ /* .debug_abbrev offset. */
+ unsigned int abbrev_off;
+ /* Corresponding hash table. */
+ htab_t abbrev_htab;
+};
+
+/* Hash table for mapping of .debug_abbrev section offsets to
+ abbrev hash tables. */
+static htab_t meta_abbrev_htab;
+/* Dummy entry used during OOM handling. */
+static struct meta_abbrev_entry meta_abbrev_fallback;
+
+/* Hash function for meta_abbrev_htab. */
+static hashval_t
+meta_abbrev_hash (const void *p)
+{
+ struct meta_abbrev_entry *m = (struct meta_abbrev_entry *)p;
+
+ return m->abbrev_off;
+}
+
+/* Equality function for meta_abbrev_htab. */
+static int
+meta_abbrev_eq (const void *p, const void *q)
+{
+ struct meta_abbrev_entry *m1 = (struct meta_abbrev_entry *)p;
+ struct meta_abbrev_entry *m2 = (struct meta_abbrev_entry *)q;
+
+ return m1->abbrev_off == m2->abbrev_off;
+}
+
+/* Delete function for meta_abbrev_htab. */
+static void
+meta_abbrev_del (void *p)
+{
+ struct meta_abbrev_entry *m = (struct meta_abbrev_entry *)p;
+
+ if (m->abbrev_htab != NULL)
+ htab_delete (m->abbrev_htab);
+}
+
+/* Collapse children of TOP_DIE to decrease memory usage. */
+static void
+collapse_child (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die,
+ unsigned int *tick)
+{
+ dw_die_ref child, next;
+ bool has_children = die->die_child != NULL;
+ unsigned int tick_diff = *tick;
+ for (child = die->die_child; child; child = next)
+ {
+ next = child->die_sib;
+ (*tick)++;
+ collapse_child (cu, top_die, child, tick);
+ }
+ if (has_children)
+ (*tick)++;
+ if (top_die == die)
+ {
+ die->die_child = NULL;
+ die->die_collapsed_children = 1;
+ }
+ else if (die->die_referenced)
+ {
+ die->die_parent = top_die;
+ if (tick_diff <= 0xffff && !die->die_intercu_referenced)
+ {
+ dw_die_ref ref;
+ void **slot;
+ if (die_collapsed_child_freelist)
+ {
+ ref = die_collapsed_child_freelist;
+ die_collapsed_child_freelist = ref->die_parent;
+ }
+ else
+ ref = pool_alloc (dw_die, offsetof (struct dw_die, die_child));
+ memcpy (ref, die, offsetof (struct dw_die, die_child));
+ ref->die_collapsed_child = 1;
+ ref->die_tag = tick_diff;
+ slot = htab_find_slot_with_hash (cu->cu_kind == CU_TYPES
+ ? types_off_htab : off_htab,
+ ref, off_hash (ref), NO_INSERT);
+ assert (slot != NULL);
+ *slot = (void *) ref;
+ memset (die, '\0', offsetof (struct dw_die, die_dup));
+ die->die_sib = die_nontoplevel_freelist;
+ die_nontoplevel_freelist = die;
+ }
+ else
+ {
+ die->die_child = NULL;
+ die->die_sib = NULL;
+ die->die_ref_seen = tick_diff;
+ }
+ }
+ else
+ {
+ memset (die, '\0', offsetof (struct dw_die, die_dup));
+ die->die_sib = die_nontoplevel_freelist;
+ die_nontoplevel_freelist = die;
+ }
+}
+
+/* Collapse children of all toplevel DIEs that can be collapsed. */
+static void
+collapse_children (dw_cu_ref cu, dw_die_ref die)
+{
+ dw_die_ref child;
+ for (child = die->die_child; child; child = child->die_sib)
+ if (child->die_named_namespace)
+ collapse_children (cu, child);
+ else if (child->die_child == NULL)
+ continue;
+ else if (child->die_nextdup == NULL
+ || (child->die_dup != NULL
+ && (die_cu (child->die_dup)->cu_kind != CU_PU
+ || child->die_dup->die_nextdup != child)))
+ {
+ unsigned int tick = 0;
+ collapse_child (cu, child, child, &tick);
+ }
+}
+
+/* Count the number of DIEs in the .debug_info section, and see if we run into
+ some limit. */
+static int
+try_debug_info (DSO *dso)
+{
+ unsigned char *ptr, *endcu, *endsec;
+ unsigned int value;
+ htab_t abbrev = NULL;
+ unsigned int last_abbrev_offset = 0;
+ struct abbrev_tag tag, *t;
+ unsigned int ndies;
+ unsigned ret = 1;
+ int kind = DEBUG_INFO;
+ bool low_mem_die_limit_hit = false;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "try_debug_info\n");
+ }
+
+ if (tracing)
+ fprintf (stderr, "Counting DIEs\n");
+
+ ndies = 0;
+ ptr = debug_sections[kind].data;
+ endsec = ptr + debug_sections[kind].size;
+ while (ptr < endsec)
+ {
+ unsigned int culen;
+ int cu_version;
+
+ /* Note header is one bigger with DWARF version 5. */
+ if (ptr + (kind == DEBUG_TYPES ? 23 : 11) > endsec)
+ {
+ error (0, 0, "%s: %s CU header too small", dso->filename,
+ debug_sections[kind].name);
+ goto fail;
+ }
+
+ endcu = ptr + 4;
+ culen = read_32 (ptr);
+ if (culen >= 0xfffffff0)
+ {
+ error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+ goto fail;
+ }
+ endcu += culen;
+
+ if (endcu > endsec)
+ {
+ error (0, 0, "%s: %s too small", dso->filename,
+ debug_sections[kind].name);
+ goto fail;
+ }
+
+ cu_version = read_16 (ptr);
+ if (kind == DEBUG_TYPES && (cu_version < 2 || cu_version > 4))
+ {
+ error (0, 0, "%s: DWARF version %d in .debug_types unhandled",
+ dso->filename, cu_version);
+ goto fail;
+ }
+ else if (cu_version < 2 || cu_version > 5)
+ {
+ error (0, 0, "%s: DWARF version %d in .debug_info unhandled",
+ dso->filename, cu_version);
+ goto fail;
+ }
+
+ if (cu_version == 5)
+ {
+ value = read_8 (ptr);
+ if (value != DW_UT_compile && value != DW_UT_partial)
+ error (0, 0, "%s: DWARF CU type %s unhandled", dso->filename,
+ get_DW_UT_str (value));
+ }
+ else
+ {
+ 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);
+ goto fail;
+ }
+ }
+
+ 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);
+ goto fail;
+ }
+ }
+ else if (read_8 (ptr) != ptr_size)
+ {
+ error (0, 0, "%s: DWARF pointer size differs between CUs",
+ dso->filename);
+ goto fail;
+ }
+
+ if (cu_version == 5)
+ {
+ /* Above we only checked for the smaller version 4 header size. */
+ if (ptr + 4 > endsec)
+ {
+ error (0, 0, "%s: %s CU version 5 header too small",
+ dso->filename, debug_sections[kind].name);
+ goto fail;
+ }
+ 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);
+ goto fail;
+ }
+ }
+
+ if (abbrev == NULL || value != last_abbrev_offset)
+ {
+ if (abbrev)
+ htab_delete (abbrev);
+ abbrev
+ = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value);
+ if (abbrev == NULL)
+ {
+ error (0, 0, "%s: Couldn't read abbrev at offset 0x%x",
+ dso->filename, value);
+ goto fail;
+ }
+ }
+ last_abbrev_offset = value;
+
+ while (ptr < endcu)
+ {
+ tag.entry = read_uleb128 (ptr);
+ if (tag.entry == 0)
+ continue;
+ if (ndies == max_die_limit)
+ {
+ error (0, 0, "%s: Too many DIEs, not optimizing",
+ dso->filename);
+ goto fail;
+ }
+ /* If we reach the DIE limit, signal the dwz caller that it
+ should retry with low_mem. */
+ if (likely (!low_mem) && ndies == low_mem_die_limit)
+ {
+ if (tracing)
+ fprintf (stderr, "Hit low-mem die-limit\n");
+ if (estimate_nr_dies () > max_die_limit)
+ /* Keep going, we still might hit the max die-limit. */
+ low_mem_die_limit_hit = true;
+ else
+ {
+ ret = 2;
+ goto fail;
+ }
+ }
+ ndies++;
+ 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);
+ goto fail;
+ }
+ ptr = skip_attrs_1 (cu_version, t, ptr);
+ }
+ }
+
+ if (low_mem_die_limit_hit)
+ ret = 2;
+ else
+ ret = 0;
+
+ fail:
+ if (abbrev)
+ htab_delete (abbrev);
+
+ return ret;
+}
+
+/* Read language attribute encoded using FORM from location PTR, return
+ pointer to location after the attribute. Assign the attribute value
+ to *LANG. */
+static unsigned char *
+read_lang (unsigned char *ptr, enum dwarf_form form,
+ enum dwarf_source_language *lang)
+{
+ bool error_p = false;
+ *lang = read_u16 (ptr, form, error_p);
+ if (unlikely (error_p))
+ {
+ *lang = 0;
+ error (0, 0, "Invalid DW_AT_language attribute, ignoring");
+ }
+ return ptr;
+}
+
+/* First phase of the DWARF compression. Parse .debug_info section
+ (for kind == DEBUG_INFO) or .debug_types section (for kind == DEBUG_TYPES)
+ for each CU in it construct internal representation for the CU
+ and its DIE tree, compute checksums of DIEs and look for duplicates. */
+static int
+read_debug_info (DSO *dso, int kind, unsigned int *die_count)
+{
+ unsigned char *ptr, *endcu, *endsec;
+ unsigned int value;
+ htab_t abbrev = NULL;
+ unsigned int last_abbrev_offset = 0;
+ unsigned int last_debug_line_off = 0;
+ struct dw_file *cu_files = NULL;
+ unsigned int cu_nfiles = 0;
+ bool note_strp_forms = multifile != NULL && !op_multifile
+ && !rd_multifile && !low_mem;
+ struct abbrev_tag tag, *t;
+ unsigned int cu_chunk = 0;
+ dw_cu_ref cu_tail = NULL, cu_collapse = NULL;
+ unsigned int cu_kind = rd_multifile ? CU_ALT
+ : kind == DEBUG_TYPES ? CU_TYPES : CU_NORMAL;
+ void *to_free = NULL;
+ int ret = 1;
+ unsigned int ndies;
+ bool low_mem_phase1 = low_mem && kind == DEBUG_INFO;
+ struct dw_cu cu_buf;
+ struct dw_die die_buf;
+ bool lang_p = odr || uni_lang_p;
+
+ odr_active_p = false;
+ if (odr)
+ odr_phase = 1;
+
+ unsigned int estimated_nr_dies = estimate_nr_dies ();
+ if (kind == DEBUG_INFO
+ && multifile_mode == 0
+ && die_count_method == estimate)
+ {
+ bool do_count = (estimated_nr_dies > max_die_limit
+ || estimated_nr_dies > low_mem_die_limit);
+ if (tracing)
+ fprintf (stderr, "Using die count estimate %u to decide whether to"
+ " count DIEs: %s\n", estimated_nr_dies,
+ do_count ? "yes" : "no");
+ if (do_count)
+ {
+ int try_ret = try_debug_info (dso);
+ if (try_ret != 0)
+ return try_ret;
+ }
+ }
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "read_debug_info %s%s\n",
+ kind == DEBUG_INFO ? ".debug_info" : ".debug_types",
+ low_mem && kind == DEBUG_INFO ? " (low-mem)" : "");
+ }
+
+ if (likely (!fi_multifile && kind != DEBUG_TYPES))
+ {
+ dup_htab = htab_try_create (100000, die_hash, die_eq, NULL);
+ if (dup_htab == NULL)
+ dwz_oom ();
+ }
+ if (unlikely (meta_abbrev_htab == NULL
+ && (op_multifile || rd_multifile || fi_multifile || low_mem)))
+ {
+ meta_abbrev_htab
+ = htab_try_create (500, meta_abbrev_hash, meta_abbrev_eq,
+ meta_abbrev_del);
+ if (meta_abbrev_htab == NULL)
+ dwz_oom ();
+ to_free = obstack_alloc (&ob2, 1);
+ }
+
+ low_mem_phase2:
+ ndies = 0;
+ ptr = debug_sections[kind].data;
+ endsec = ptr + debug_sections[kind].size;
+ while (ptr < endsec)
+ {
+ unsigned int cu_offset = ptr - debug_sections[kind].data;
+ unsigned int tick = 0, culen;
+ int cu_version;
+ dw_cu_ref cu;
+ dw_die_ref *diep, parent, die;
+ bool present;
+ unsigned int debug_line_off;
+ unsigned int type_offset = 0;
+
+ /* Note header is one bigger with DWARF version 5. */
+ if (ptr + (kind == DEBUG_TYPES ? 23 : 11) > endsec)
+ {
+ error (0, 0, "%s: %s CU header too small", dso->filename,
+ debug_sections[kind].name);
+ goto fail;
+ }
+
+ endcu = ptr + 4;
+ culen = read_32 (ptr);
+ if (culen >= 0xfffffff0)
+ {
+ error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+ goto fail;
+ }
+ endcu += culen;
+
+ if (endcu > endsec)
+ {
+ error (0, 0, "%s: %s too small", dso->filename,
+ debug_sections[kind].name);
+ goto fail;
+ }
+
+ cu_version = read_16 (ptr);
+ if (kind == DEBUG_TYPES && (cu_version < 2 || cu_version > 4))
+ {
+ error (0, 0, "%s: DWARF version %d in .debug_types unhandled",
+ dso->filename, cu_version);
+ goto fail;
+ }
+ else if (cu_version < 2 || cu_version > 5)
+ {
+ error (0, 0, "%s: DWARF version %d in .debug_info unhandled",
+ dso->filename, cu_version);
+ goto fail;
+ }
+
+ if (cu_version == 5)
+ {
+ value = read_8 (ptr);
+ if (value != DW_UT_compile && value != DW_UT_partial)
+ {
+ error (0, 0, "%s: DWARF CU type %s unhandled", dso->filename,
+ get_DW_UT_str (value));
+ goto fail;
+ }
+ }
+ else
+ {
+ 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);
+ goto fail;
+ }
+ }
+
+ 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);
+ goto fail;
+ }
+ }
+ else if (read_8 (ptr) != ptr_size)
+ {
+ error (0, 0, "%s: DWARF pointer size differs between CUs",
+ dso->filename);
+ goto fail;
+ }
+
+ if (cu_version == 5)
+ {
+ /* Above we only checked for the smaller version 4 header size. */
+ if (ptr + 4 > endsec)
+ {
+ error (0, 0, "%s: %s CU version 5 header too small",
+ dso->filename, debug_sections[kind].name);
+ goto fail;
+ }
+ 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);
+ goto fail;
+ }
+ }
+
+ if (unlikely (op_multifile))
+ {
+ if (ptr == endcu)
+ {
+ dw_cu_ref cuf = cu_tail ? cu_tail->cu_next : first_cu;
+ /* Inside of optimize_multifile, DIE hashes are computed
+ only after all the CUs from a particular DSO or
+ executable have been parsed, as we follow
+ DW_FORM_ref_addr then. */
+ for (cu = cuf; cu; cu = cu->cu_next)
+ if (checksum_die (dso, cu, NULL, cu->cu_die))
+ goto fail;
+
+ for (cu = cuf; cu; cu = cu->cu_next)
+ checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL);
+
+ if (dump_dies_p)
+ for (cu = cuf; cu; cu = cu->cu_next)
+ dump_dies (0, cu->cu_die);
+
+ for (cu = cuf; cu; cu = cu->cu_next)
+ if (find_dups (cu->cu_die))
+ goto fail;
+
+ for (cu = cuf; cu; cu = cu->cu_next)
+ remove_unneeded (cu, cu->cu_die, 0);
+ for (cu = cuf; cu; cu = cu->cu_next)
+ remove_unneeded (cu, cu->cu_die, 1);
+ for (cu = cuf; cu; cu = cu->cu_next)
+ remove_unneeded (cu, cu->cu_die, 2);
+
+ if (cu_collapse == NULL)
+ cu_collapse = first_cu;
+ while (cu_collapse->cu_chunk < cu_chunk)
+ {
+ collapse_children (cu_collapse, cu_collapse->cu_die);
+ cu_collapse = cu_collapse->cu_next;
+ }
+
+ cu_tail = last_cu;
+ cu_chunk++;
+ continue;
+ }
+ }
+ else
+ cu_chunk++;
+
+ if (unlikely (meta_abbrev_htab != NULL))
+ {
+ struct meta_abbrev_entry m, *mp;
+ void **slot;
+ m.abbrev_off = value;
+ slot = htab_find_slot_with_hash (meta_abbrev_htab, &m,
+ m.abbrev_off, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ else if (*slot != NULL)
+ abbrev = ((struct meta_abbrev_entry *) *slot)->abbrev_htab;
+ else
+ {
+ *slot = (void *) &meta_abbrev_fallback;
+ abbrev
+ = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value);
+ if (abbrev == NULL)
+ goto fail;
+ mp = (struct meta_abbrev_entry *)
+ obstack_alloc (&ob2, sizeof (*mp));
+ mp->abbrev_off = value;
+ mp->abbrev_htab = abbrev;
+ *slot = (void *) mp;
+ }
+ }
+ else if (abbrev == NULL || value != last_abbrev_offset)
+ {
+ if (abbrev)
+ htab_delete (abbrev);
+ abbrev
+ = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value);
+ if (abbrev == NULL)
+ {
+ error (0, 0, "%s: Couldn't read abbrev at offset 0x%x",
+ dso->filename, value);
+
+ goto fail;
+ }
+ }
+ last_abbrev_offset = value;
+
+ if (unlikely (kind == DEBUG_TYPES))
+ {
+ ptr += 8;
+ type_offset = read_32 (ptr);
+ }
+
+ if (unlikely (low_mem_phase1))
+ cu = &cu_buf;
+ else
+ cu = pool_alloc (dw_cu, sizeof (struct dw_cu));
+ memset (cu, '\0', sizeof (*cu));
+ cu->cu_kind = cu_kind;
+ cu->cu_offset = cu_offset;
+ cu->cu_version = cu_version;
+ cu->cu_chunk = cu_chunk;
+ if (unlikely (op_multifile || low_mem))
+ cu->cu_abbrev = abbrev;
+ diep = &cu->cu_die;
+ parent = NULL;
+ if (unlikely (low_mem_phase1))
+ ;
+ else 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[kind].data;
+
+ tag.entry = read_uleb128 (ptr);
+ if (tag.entry == 0)
+ {
+ if (unlikely (low_mem_phase1))
+ continue;
+ if (parent)
+ {
+ diep = &parent->die_sib;
+ parent->u.p1.die_exit = tick++;
+ if (parent->die_root == 0)
+ parent = parent->die_parent;
+ else
+ parent = NULL;
+ }
+ else
+ diep = NULL;
+ continue;
+ }
+ if (diep == NULL)
+ {
+ error (0, 0, "%s: Wrong %s DIE tree", dso->filename,
+ debug_sections[kind].name);
+ goto fail;
+ }
+ 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);
+ goto fail;
+ }
+ if (likely (!op_multifile && !rd_multifile && !fi_multifile)
+ && likely (kind == DEBUG_INFO))
+ {
+ if (ndies == max_die_limit)
+ {
+ error (0, 0, "%s: Too many DIEs, not optimizing",
+ dso->filename);
+ goto fail;
+ }
+ /* If we reach the DIE limit, silently signal the dwz
+ caller that it should retry with low_mem. */
+ if (likely (!low_mem) && ndies == low_mem_die_limit)
+ {
+ if (tracing)
+ fprintf (stderr, "Hit low-mem die-limit\n");
+ ret = 2;
+ goto fail;
+ }
+ }
+ ndies++;
+ if (unlikely (low_mem_phase1))
+ die = &die_buf;
+ else if (parent == NULL
+ || parent->die_root
+ || parent->die_named_namespace)
+ {
+ die = pool_alloc (dw_die, sizeof (struct dw_die));
+ memset (die, '\0', sizeof (struct dw_die));
+ die->die_toplevel = 1;
+ }
+ else
+ {
+ if (die_nontoplevel_freelist)
+ {
+ die = die_nontoplevel_freelist;
+ die_nontoplevel_freelist = die->die_sib;
+ }
+ else
+ die = pool_alloc (dw_die, offsetof (struct dw_die, die_dup));
+ memset (die, '\0', offsetof (struct dw_die, die_dup));
+ }
+ *diep = die;
+ die->die_tag = t->tag;
+ die->die_abbrev = t;
+ die->die_offset = die_offset;
+ if (parent)
+ die->die_parent = parent;
+ else
+ {
+ die->die_root = 1;
+ die->die_parent = (dw_die_ref) cu;
+ }
+ die->u.p1.die_enter = tick;
+ die->u.p1.die_exit = tick++;
+ if (likely (!low_mem_phase1))
+ {
+ 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);
+ goto fail;
+ }
+ }
+
+ /* Get length of expr/blocks first. Canonicalize all,
+ except exprloc, to DW_FORM_block1. */
+ switch (form)
+ {
+ 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:
+ break;
+ }
+
+ if (unlikely (low_mem_phase1)
+ && add_locexpr_dummy_dies (dso, cu, die, ptr, form,
+ t->attr[i].attr, len))
+ goto fail;
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ if (unlikely (low_mem_phase1))
+ {
+ unsigned int offset
+ = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4);
+ add_dummy_die (cu, offset);
+ }
+ 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_implicit_const:
+ if (lang_p
+ && (die->die_tag == DW_TAG_compile_unit
+ || die->die_tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_language)
+ cu->lang = t->values[i];
+ break;
+ case DW_FORM_data1:
+ if (lang_p
+ && (die->die_tag == DW_TAG_compile_unit
+ || die->die_tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_language)
+ cu->lang = *ptr;
+ /* FALLTHRU */
+ case DW_FORM_ref1:
+ case DW_FORM_flag:
+ ++ptr;
+ break;
+ case DW_FORM_data2:
+ if (lang_p
+ && (die->die_tag == DW_TAG_compile_unit
+ || die->die_tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_language)
+ cu->lang = do_read_16 (ptr);
+ /* FALLTHRU */
+ case DW_FORM_ref2:
+ ptr += 2;
+ break;
+ case DW_FORM_data4:
+ if (lang_p
+ && (die->die_tag == DW_TAG_compile_unit
+ || die->die_tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_language)
+ read_lang (ptr, form, &cu->lang);
+ /* FALLTHRU */
+ case DW_FORM_ref4:
+ case DW_FORM_sec_offset:
+ ptr += 4;
+ break;
+ case DW_FORM_data8:
+ if (lang_p
+ && (die->die_tag == DW_TAG_compile_unit
+ || die->die_tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_language)
+ read_lang (ptr, form, &cu->lang);
+ /* FALLTHRU */
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ ptr += 8;
+ break;
+ case DW_FORM_data16:
+ ptr += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ if (lang_p
+ && (die->die_tag == DW_TAG_compile_unit
+ || die->die_tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_language)
+ {
+ ptr = read_lang (ptr, form, &cu->lang);
+ break;
+ }
+ /* FALLTHRU */
+ case DW_FORM_ref_udata:
+ skip_leb128 (ptr);
+ break;
+ case DW_FORM_strp:
+ if (t->attr[i].attr == DW_AT_name
+ && (die->die_tag == DW_TAG_namespace
+ || die->die_tag == DW_TAG_module)
+ && !die->die_root
+ && (die->die_parent->die_root
+ || die->die_parent->die_named_namespace))
+ die->die_named_namespace = 1;
+ if (note_strp_forms)
+ note_strp_offset (read_32 (ptr));
+ else
+ ptr += 4;
+ break;
+ case DW_FORM_line_strp:
+ if (t->attr[i].attr == DW_AT_name
+ && (die->die_tag == DW_TAG_namespace
+ || die->die_tag == DW_TAG_module)
+ && !die->die_root
+ && (die->die_parent->die_root
+ || die->die_parent->die_named_namespace))
+ die->die_named_namespace = 1;
+ /* Don't note strp, different string table. */
+ ptr += 4;
+ 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_root
+ && (die->die_parent->die_root
+ || die->die_parent->die_named_namespace))
+ die->die_named_namespace = 1;
+ break;
+ case DW_FORM_indirect:
+ abort ();
+ /* All expr/blocks lengths already handled above.
+ Just canonicalize exprloc to block1 too. */
+ case DW_FORM_exprloc:
+ form = DW_FORM_block1;
+ break;
+ case DW_FORM_block1:
+ break;
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_block:
+ abort ();
+ default:
+ error (0, 0, "%s: Unknown DWARF %s at DIE [%x]",
+ dso->filename, get_DW_FORM_str (form), die_offset);
+ goto fail;
+ }
+
+ if (ptr > endcu)
+ {
+ error (0, 0, "%s: Attributes extend beyond end of CU "
+ "for DIE [%x]",
+ dso->filename, die_offset);
+ goto fail;
+ }
+
+ if (form == DW_FORM_block1)
+ {
+ if (len >= (size_t) (endcu - ptr))
+ {
+ error (0, 0, "%s: Attributes extend beyond end of CU "
+ "for DIE [%x]",
+ dso->filename, die_offset);
+ goto fail;
+ }
+
+ if (t->attr[i].attr > DW_AT_loclists_base
+ && (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 %s with "
+ "block DW_FORM for DIE [%x]",
+ dso->filename, get_DW_AT_str (t->attr[i].attr),
+ die_offset);
+ goto fail;
+ }
+
+ ptr += len;
+ }
+ }
+ die->die_size = (ptr - debug_sections[kind].data)
+ - die_offset;
+ if (unlikely (low_mem))
+ {
+ if (low_mem_phase1)
+ continue;
+ if (off_htab != NULL && kind == DEBUG_INFO)
+ {
+ void **slot
+ = htab_find_slot_with_hash (off_htab, die, off_hash (die),
+ INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot != NULL)
+ {
+ dw_die_ref ref = (dw_die_ref) *slot;
+ assert (ref->die_collapsed_child);
+ die->die_referenced = 1;
+ die->die_intercu_referenced = 1;
+ memset (ref, '\0', offsetof (struct dw_die, die_child));
+ ref->die_parent = die_collapsed_child_freelist;
+ die_collapsed_child_freelist = ref;
+ }
+ *slot = (void *) die;
+ continue;
+ }
+ }
+
+ off_htab_add_die (cu, die, die_count);
+ }
+
+ if (unlikely (low_mem_phase1))
+ continue;
+
+ 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_tag != DW_TAG_type_unit)
+ || cu->cu_die->die_sib != NULL)
+ {
+ error (0, 0, "%s: %s section chunk doesn't contain a single"
+ " compile_unit or partial_unit", dso->filename,
+ debug_sections[kind].name);
+ goto fail;
+ }
+
+ cu->cu_comp_dir = get_AT_string (cu->cu_die, DW_AT_comp_dir);
+ if (skip_producers_p
+ && skip_producer (get_AT_string (cu->cu_die, DW_AT_producer)))
+ {
+ cu->cu_die->die_remove = 1;
+ continue;
+ }
+ enum dwarf_form form;
+ debug_line_off
+ = get_AT_int (cu->cu_die, DW_AT_stmt_list, &present, &form);
+ if (present)
+ {
+ if (!(form == DW_FORM_sec_offset || form == DW_FORM_data4))
+ {
+ error (0, 0, "%s: DW_AT_stmt_list not DW_FORM_sec_offset or"
+ " DW_FORM_data4", dso->filename);
+ goto fail;
+ }
+
+ if (cu_files != NULL && last_debug_line_off == debug_line_off)
+ {
+ cu->cu_nfiles = cu_nfiles;
+ cu->cu_files = cu_files;
+ }
+ else
+ {
+ if (read_debug_line (dso, cu, debug_line_off))
+ goto fail;
+ cu_nfiles = cu->cu_nfiles;
+ cu_files = cu->cu_files;
+ last_debug_line_off = debug_line_off;
+ }
+ }
+
+ if (likely (!op_multifile && !rd_multifile && !fi_multifile)
+ && likely (kind == DEBUG_INFO))
+ {
+ if (checksum_die (dso, cu, NULL, cu->cu_die))
+ goto fail;
+ checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL);
+ if (odr)
+ {
+ dw_die_ref die;
+ FOREACH_LOW_TOPLEVEL_DIE_IN_CU (die, cu)
+ {
+ if (die->die_ck_state != CK_KNOWN)
+ continue;
+ if (die_odr_state (die) != ODR_NONE)
+ die->u.p1.die_ref_hash = die->u.p1.die_hash;
+ else
+ die->die_ref_hash_computed = 0;
+ }
+ checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL);
+ }
+
+ if (dump_dies_p)
+ dump_dies (0, cu->cu_die);
+
+ if (find_dups (cu->cu_die))
+ goto fail;
+ }
+ if (unlikely (kind == DEBUG_TYPES))
+ {
+ dw_die_ref ref = off_htab_lookup (cu, cu->cu_offset + type_offset);
+ if (ref == NULL)
+ {
+ error (0, 0, "%s: Couldn't find DIE at [%x] "
+ "referenced by type_offset from cu DIE at [%x]",
+ dso->filename, cu->cu_offset + type_offset,
+ cu->cu_die->die_offset);
+ goto fail;
+ }
+ if (unlikely (low_mem))
+ {
+ ref->die_referenced = 1;
+ ref->die_intercu_referenced = 1;
+ }
+ }
+ if (unlikely (low_mem))
+ {
+ remove_unneeded (cu, cu->cu_die, 1);
+ remove_unneeded (cu, cu->cu_die, 2);
+ collapse_children (cu, cu->cu_die);
+ }
+ }
+
+ if (unlikely (low_mem_phase1))
+ {
+ low_mem_phase1 = false;
+ cu_chunk = 0;
+ goto low_mem_phase2;
+ }
+
+ if (unlikely (low_mem))
+ ;
+ else if (unlikely (meta_abbrev_htab != NULL))
+ {
+ dw_cu_ref cu;
+
+ if (unlikely (op_multifile))
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ cu->cu_abbrev = NULL;
+ htab_delete (meta_abbrev_htab);
+ meta_abbrev_htab = NULL;
+ obstack_free (&ob2, to_free);
+ abbrev = NULL;
+ }
+ else if (abbrev)
+ htab_delete (abbrev);
+
+ if (unlikely (kind == DEBUG_TYPES))
+ return 0;
+
+ if (unlikely (rd_multifile || fi_multifile))
+ {
+ dw_cu_ref cu;
+
+ /* Inside of read_multifile, DIE hashes are computed
+ only after all the PUs are parsed, as we follow
+ DW_FORM_ref_addr then. */
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (checksum_die (dso, cu, NULL, cu->cu_die))
+ goto fail;
+
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL);
+
+ if (dump_dies_p)
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ dump_dies (0, cu->cu_die);
+
+ if (rd_multifile)
+ {
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (find_dups (cu->cu_die))
+ goto fail;
+ }
+ else
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (find_dups_fi (cu->cu_die))
+ goto fail;
+
+ return 0;
+ }
+
+ if (tracing)
+ {
+ if (op_multifile)
+ fprintf (stderr, "Die count: %u\n", ndies);
+ else
+ fprintf (stderr, "Die count: %u, %.2f%% of estimate\n", ndies,
+ (double)ndies / ((double)estimated_nr_dies / 100));
+ }
+ if (tracing)
+ htab_report (off_htab, "off_htab post-parsing");
+ if (stats_p)
+ stats->die_count = ndies;
+ if (die_count)
+ *die_count = ndies;
+ htab_delete (dup_htab);
+ dup_htab = NULL;
+ return 0;
+fail:
+ if (unlikely (meta_abbrev_htab != NULL))
+ {
+ dw_cu_ref cu;
+
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ cu->cu_abbrev = NULL;
+ htab_delete (meta_abbrev_htab);
+ meta_abbrev_htab = NULL;
+ obstack_free (&ob2, to_free);
+ }
+ else if (abbrev)
+ htab_delete (abbrev);
+ if (dup_htab && kind == DEBUG_INFO)
+ {
+ htab_delete (dup_htab);
+ dup_htab = NULL;
+ }
+ return ret;
+}
+
+/* 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;
+ dw_cu_ref last_cu1 = NULL, last_cu2 = NULL;
+ ref1 = die1;
+ ref2 = die2;
+ if (odr_active_p && odr_mode != ODR_BASIC)
+ {
+ while (ref1 && die_odr_state (ref1) == ODR_DECL)
+ ref1 = ref1->die_nextdup;
+ if (ref1 == NULL)
+ ref1 = die1;
+ while (ref2 && die_odr_state (ref2) == ODR_DECL)
+ ref2 = ref2->die_nextdup;
+ if (ref2 == NULL)
+ ref2 = die2;
+ }
+ for (;; ref1 = ref1->die_nextdup, ref2 = ref2->die_nextdup)
+ {
+ dw_cu_ref ref1cu = NULL;
+ dw_cu_ref ref2cu = NULL;
+ while (ref1 && (ref1cu = die_cu (ref1)) == last_cu1)
+ ref1 = ref1->die_nextdup;
+ while (ref2 && (ref2cu = die_cu (ref2)) == last_cu2)
+ ref2 = ref2->die_nextdup;
+ if (ref1 == NULL || ref2 == NULL)
+ break;
+ last_cu1 = ref1cu;
+ last_cu2 = ref2cu;
+ 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;
+}
+
+/* Add duplicate chain for DIE to VEC. */
+static void
+partition_found_dups (dw_die_ref die, struct obstack *vec)
+{
+ assert (die->die_ck_state == CK_KNOWN);
+ obstack_ptr_grow (vec, die);
+ if (unlikely (verify_dups_p))
+ verify_dups (die, true);
+
+ if (stats_p)
+ {
+ uint64_t length = 0;
+ for (; die; die = die->die_nextdup)
+ length++;
+ stats->dup_cnt += length;
+ stats->dup_chain_max_length = MAX (stats->dup_chain_max_length, length);
+ stats->dup_chain_cnt++;
+ }
+}
+
+/* Sort duplication chain for HEAD, assuming the chain was formed by
+ die_eq. */
+static void
+sort_dups (dw_die_ref head)
+{
+ dw_die_ref prev = NULL, die, next;
+ /* Sort the die_nextdup list by increasing die_cu ()->cu_chunk.
+ When it is originally added, child has the lowest
+ cu_offset, then the DIEs are sorted in the linked list
+ from highest cu_offset down to lowest or second lowest. */
+ for (die = head->die_nextdup; die; prev = die, die = next)
+ {
+ next = die->die_nextdup;
+ die->die_nextdup = prev;
+ }
+ head->die_nextdup = prev;
+}
+
+/* Merge duplicate chains D and D2, and return the head of the merged
+chain. */
+static dw_die_ref
+merge_dups (dw_die_ref d, dw_die_ref d2)
+{
+ if (d == NULL)
+ return d2;
+ if (d2 == NULL)
+ return d;
+
+ dw_die_ref tail = NULL;
+ dw_die_ref head = NULL;
+ while (true)
+ {
+ dw_die_ref next;
+ if (d && d2)
+ {
+ if (d->die_offset < d2->die_offset)
+ {
+ next = d;
+ d = d->die_nextdup;
+ }
+ else
+ {
+ next = d2;
+ d2 = d2->die_nextdup;
+ }
+ }
+ else if (d)
+ {
+ next = d;
+ d = d->die_nextdup;
+ }
+ else if (d2)
+ {
+ next = d2;
+ d2 = d2->die_nextdup;
+ }
+ else
+ break;
+ if (!head)
+ head = next;
+ if (tail)
+ tail->die_nextdup = next;
+ tail = next;
+ }
+
+ for (d = head; d; d = d->die_nextdup)
+ if (d == head)
+ d->die_dup = NULL;
+ else
+ d->die_dup = head;
+
+ if (unlikely (verify_dups_p))
+ verify_dups (head, true);
+ return head;
+}
+
+static void
+mark_singletons (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die,
+ struct obstack *vec)
+{
+ struct abbrev_tag *t;
+ unsigned int i;
+ unsigned char *ptr;
+ dw_die_ref child;
+ bool found;
+
+ t = die->die_abbrev;
+
+ found = false;
+ for (i = 0; i < t->nattr; ++i)
+ {
+ struct abbrev_attr *attr = &t->attr[i];
+ if (attr->attr == DW_AT_sibling)
+ continue;
+ switch (attr->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:
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ goto handle_children;
+
+ if (unlikely (cu->cu_kind == CU_TYPES))
+ ptr = debug_sections[DEBUG_TYPES].data;
+ else
+ ptr = debug_sections[DEBUG_INFO].data;
+ ptr += die->die_offset;
+ skip_leb128 (ptr);
+ for (i = 0; i < t->nattr; ++i)
+ {
+ struct abbrev_attr *attr = &t->attr[i];
+ uint32_t form = attr->form;
+ uint64_t value;
+ dw_die_ref ref, reft;
+
+ while (form == DW_FORM_indirect)
+ form = read_uleb128 (ptr);
+
+ switch (form)
+ {
+ 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)
+ break;
+ ref = off_htab_lookup (cu, cu->cu_offset + value);
+ if (!ref->die_collapsed_child
+ && 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_root
+ && 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_dup != NULL || reft->die_nextdup != NULL)
+ break;
+ if (reft->die_ref_seen)
+ break;
+ reft->die_ref_seen = 1;
+ partition_found_dups (reft, vec);
+ mark_singletons (die_cu (reft), reft, reft, vec);
+ break;
+ default:
+ ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr);
+ break;
+ }
+ }
+
+ handle_children:
+ for (child = die->die_child; child; child = child->die_sib)
+ mark_singletons (cu, top_die, child, vec);
+}
+
+/* Split maximal duplicate chain DIE into smaller chains that contain
+ structurally equal defs, and combine the decls with one of those.
+ Return the first chain, and call partition_found_dups for the others. */
+static dw_die_ref
+split_dups (dw_die_ref die, struct obstack *vec)
+{
+ dw_die_ref res = NULL;
+
+ /* Count the DIEs in the duplicate chain. */
+ unsigned count = 0;
+ dw_die_ref d;
+ for (d = die; d; d = d->die_nextdup)
+ count++;
+ assert (count >= 2);
+
+ /* Break up the duplicate chain. */
+ dw_die_ref arr[count];
+ unsigned int i;
+ for (d = die, i = 0; d; d = d->die_nextdup, i++)
+ arr[i] = d;
+ for (i = 0; i < count; i++)
+ {
+ d = arr[i];
+ d->die_nextdup = NULL;
+ d->die_dup = NULL;
+ }
+
+ /* Build decls duplicate chain. */
+ dw_die_ref tail = NULL;
+ dw_die_ref head = NULL;
+ for (i = 0; i < count; i++)
+ {
+ d = arr[i];
+ if (die_odr_state (d) != ODR_DECL)
+ continue;
+ if (!head)
+ head = d;
+ if (tail)
+ tail->die_nextdup = d;
+ if (d != head)
+ d->die_dup = head;
+ tail = d;
+ }
+ dw_die_ref decls = head;
+
+ /* Build def duplicate chains. */
+ unsigned int j;
+ dw_die_ref d2;
+ for (i = 0; i < count; i++)
+ {
+ d = arr[i];
+ if (d->die_dup || d->die_nextdup
+ || die_odr_state (d) == ODR_DECL)
+ continue;
+ tail = d;
+ for (j = i + 1; j < count; j++)
+ {
+ d2 = arr[j];
+ if (d2->die_dup || d2->die_nextdup
+ || die_odr_state (d2) == ODR_DECL)
+ continue;
+ die_eq (d, d2);
+ }
+ sort_dups (d);
+ }
+
+ if (odr_mode != ODR_BASIC)
+ {
+ /* Find the first duplicate chain containing defs. */
+ dw_die_ref def = NULL;
+ for (i = 0; i < count; i++)
+ {
+ d = arr[i];
+ if (die_odr_state (d) == ODR_DECL
+ || d->die_dup != NULL)
+ continue;
+ def = d;
+ break;
+ }
+
+ /* Merge the def chain with the decls. */
+ merge_dups (def, decls);
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ d = arr[i];
+ if (d->die_dup == NULL)
+ /* If DIE is now either:
+ - no longer part of a duplicate chain, or
+ - head of a duplicate chain,
+ don't remove it. */
+ d->die_remove = 0;
+ }
+
+ /* Notice the new duplicate chains. */
+ for (i = 0; i < count; i++)
+ {
+ d = arr[i];
+ if (d->die_dup != NULL)
+ continue;
+ if (res == NULL)
+ res = d;
+ else
+ partition_found_dups (d, vec);
+ }
+ return res;
+}
+
+/* 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 (child->die_nextdup != NULL
+ && child->die_dup == NULL
+ && child->die_offset != -1U)
+ {
+ dw_die_ref die;
+
+ if (unlikely (op_multifile))
+ {
+ /* If all the dups are from the same DSO or executable,
+ there is nothing in it to optimize in between different
+ objects. */
+ unsigned int cu_chunk = die_cu (child)->cu_chunk;
+ for (die = child->die_nextdup; die; die = die->die_nextdup)
+ if (die_cu (die)->cu_chunk != cu_chunk)
+ break;
+ if (die == NULL)
+ continue;
+ }
+ sort_dups (child);
+ partition_found_dups (child, vec);
+ }
+ else if (child->die_named_namespace)
+ partition_find_dups (vec, child);
+ }
+}
+
+/* Reorder duplicate chain DIE to make sure it doesn't start with a decl. */
+static dw_die_ref
+reorder_dups (dw_die_ref die)
+{
+ unsigned decl_count = 0;
+ unsigned def_count = 0;
+ dw_die_ref d;
+
+ if (die_odr_state (die) == ODR_NONE)
+ return die;
+
+ for (d = die; d; d = d->die_nextdup)
+ {
+ if (die_odr_state (d) == ODR_DECL)
+ decl_count++;
+ else
+ def_count++;
+ }
+ if (def_count == 0 || decl_count == 0)
+ return die;
+
+ if (die_odr_state (die) != ODR_DECL)
+ return die;
+
+ dw_die_ref def = NULL;
+ dw_die_ref prev = NULL;
+ for (d = die; d; prev = d, d = d->die_nextdup)
+ {
+ if (die_odr_state (d) == ODR_DECL)
+ continue;
+ def = d;
+ break;
+ }
+
+ assert (!die->die_remove);
+ assert (def->die_remove);
+ def->die_remove = 0;
+ die->die_remove = 1;
+ def->die_ref_seen = die->die_ref_seen;
+ dw_die_ref next = def->die_nextdup;
+ if (prev)
+ prev->die_nextdup = next;
+ def->die_nextdup = die;
+ for (d = def; d; prev = d, d = d->die_nextdup)
+ {
+ if (d == def)
+ d->die_dup = NULL;
+ else
+ d->die_dup = def;
+ }
+
+ if (unlikely (verify_dups_p))
+ verify_dups (def, false);
+ return def;
+}
+
+/* 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 = pool_alloc (dw_die, sizeof (struct dw_die));
+ memset (new_die, '\0', sizeof (*new_die));
+ new_die->die_toplevel = 1;
+ die->die_dup = new_die;
+ new_die->die_nextdup = die;
+ if (!die->die_op_type_referenced)
+ die->die_remove = 1;
+ }
+ else
+ {
+ new_die = pool_alloc (dw_die, offsetof (struct dw_die, die_dup));
+ memset (new_die, '\0', offsetof (struct dw_die, die_dup));
+ }
+ new_die->die_parent = parent;
+ new_die->die_tag = die->die_tag;
+ new_die->die_offset = -1U;
+ 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;
+}
+
+/* If all DIEs in the duplication chain DIE are in CUs with the same
+ language, return that language. Otherwise, return 0. */
+static enum dwarf_source_language
+partition_lang (dw_die_ref die)
+{
+ enum dwarf_source_language lang;
+ dw_die_ref d;
+
+ if (die == NULL)
+ return 0;
+
+ lang = die_cu (die)->lang;
+ switch (lang)
+ {
+ case DW_LANG_C_plus_plus:
+ case DW_LANG_C_plus_plus_03:
+ case DW_LANG_C_plus_plus_11:
+ case DW_LANG_C_plus_plus_14:
+ break;
+ default:
+ return 0;
+ }
+
+ for (d = die->die_nextdup; d; d = d->die_nextdup)
+ if (die_cu (d)->lang != lang)
+ return 0;
+
+ return lang;
+}
+
+/* Return how many bytes we need to encode VAL. */
+static unsigned int
+nr_bytes_for (uint64_t val)
+{
+ unsigned int n;
+
+ if (val == 0)
+ return 1;
+
+ for (n = 0; val > 0; n++)
+ val = val >> 8;
+
+ return n;
+}
+
+/* Return true if duplicate chains REF1 and REF2 have the same set of
+ referrer CUs. If so, return the number of unique referrer CUs
+ in *CNT. */
+static inline unsigned int FORCE_INLINE
+same_ref_cus_p (dw_die_ref ref1, dw_die_ref ref2, size_t *cnt)
+{
+ dw_cu_ref last_cu1 = NULL, last_cu2 = NULL;
+
+ *cnt = 0;
+
+ if (odr_active_p && odr_mode != ODR_BASIC)
+ {
+ dw_die_ref orig_ref1 = ref1, orig_ref2 = ref2;
+ while (ref1 && die_odr_state (ref1) == ODR_DECL)
+ ref1 = ref1->die_nextdup;
+ if (ref1 == NULL)
+ ref1 = orig_ref1;
+ while (ref2 && die_odr_state (ref2) == ODR_DECL)
+ ref2 = ref2->die_nextdup;
+ if (ref2 == NULL)
+ ref2 = orig_ref2;
+ }
+ for (;; ref1 = ref1->die_nextdup, ref2 = ref2->die_nextdup)
+ {
+ dw_cu_ref ref1cu = NULL;
+ dw_cu_ref ref2cu = NULL;
+
+ while (ref1 && (ref1cu = die_cu (ref1)) == last_cu1)
+ ref1 = ref1->die_nextdup;
+ while (ref2 && (ref2cu = die_cu (ref2)) == last_cu2)
+ ref2 = ref2->die_nextdup;
+ if (ref1 == NULL || ref2 == NULL)
+ break;
+
+ last_cu1 = ref1cu;
+ last_cu2 = ref2cu;
+
+ if (last_cu1 != last_cu2)
+ break;
+ else
+ (*cnt)++;
+ }
+
+ if (ref1 || ref2)
+ return false;
+
+ return true;
+}
+
+/* Return the number of unique referrer CUs in duplicate chain REF. */
+static inline size_t FORCE_INLINE
+cnt_ref_cus (dw_die_ref ref)
+{
+ dw_cu_ref last_cu1 = NULL;
+ size_t cnt = 0;
+
+ for (;; ref = ref->die_nextdup)
+ {
+ dw_cu_ref refcu = NULL;
+ while (ref && (refcu = die_cu (ref)) == last_cu1)
+ ref = ref->die_nextdup;
+ if (ref == NULL)
+ break;
+ last_cu1 = refcu;
+ cnt++;
+ }
+
+ return cnt;
+}
+
+/* Helper function of partition_dups_1. Decide what DIEs matching in
+ multiple CUs might be worthwhile to be moved into partial units,
+ construct those partial units. */
+static bool
+partition_dups_1 (dw_die_ref *arr, size_t nr_partitions, size_t *partitions,
+ dw_cu_ref *first_partial_cu,
+ dw_cu_ref *last_partial_cu,
+ bool second_phase)
+{
+ size_t i, j, cnt;
+ bool ret = false;
+ size_t idx = 0;
+ for (idx = 0; idx < nr_partitions * 2; idx += 2)
+ {
+ i = partitions[idx];
+ cnt = partitions[idx + 1];
+ j = partitions[idx + 2];
+
+ if (arr[i]->die_dup != NULL)
+ continue;
+
+ dw_die_ref ref;
+ size_t size = 0, k, orig_size, new_size, namespaces = 0;
+ unsigned int force = 0;
+ enum dwarf_source_language part_lang
+ = gen_cu_p ? partition_lang (arr[i]) : 0;
+ for (k = i; k < j; k++)
+ {
+ if (second_phase && arr[k]->die_ref_seen)
+ force++;
+ size += calc_sizes (arr[k]);
+ for (ref = arr[k]->die_parent;
+ ref->die_named_namespace && ref->die_dup == NULL;
+ ref = ref->die_parent)
+ {
+ ref->die_dup = arr[k];
+ namespaces++;
+ }
+ }
+ /* If during second_phase there are some DIEs we want to force
+ into a partial unit because they are referenced from something
+ already forced into a partial unit, but also some DIEs with
+ the same set of referrers, try to see if we can put also those
+ into the partial unit. They can be put there only if they
+ don't refer to DIEs that won't be put into partial units. */
+ if (unlikely (partition_dups_opt)
+ && second_phase && force && force < j - i)
+ {
+ /* First optimistically assume all such DIEs can be put there,
+ thus mark all such DIEs as going to be included, so that
+ even if one of those DIEs references another one from those
+ DIEs it can be included. */
+ for (k = i; k < j; k++)
+ {
+ assert (arr[k]->die_ref_seen < 2);
+ if (arr[k]->die_ref_seen == 0)
+ arr[k]->die_ref_seen = 2;
+ }
+ for (k = i; k < j; k++)
+ if (arr[k]->die_ref_seen == 2
+ && !mark_refs (die_cu (arr[k]), arr[k], arr[k],
+ (MARK_REFS_FOLLOW_DUPS | MARK_REFS_RETURN_VAL)))
+ break;
+ /* If that is not possible and some DIEs couldn't be included,
+ fallback to assume other DIEs won't be included. */
+ if (k < j)
+ {
+ for (k = i; k < j; k++)
+ if (arr[k]->die_ref_seen == 2)
+ arr[k]->die_ref_seen = 0;
+ for (k = i; k < j; k++)
+ if (arr[k]->die_ref_seen == 0)
+ {
+ arr[k]->die_ref_seen = 2;
+ if (!mark_refs (die_cu (arr[k]), arr[k], arr[k],
+ (MARK_REFS_FOLLOW_DUPS
+ | MARK_REFS_RETURN_VAL)))
+ arr[k]->die_ref_seen = 0;
+ }
+ }
+ }
+ if (namespaces)
+ {
+ for (k = i; k < j; k++)
+ for (ref = arr[k]->die_parent; ref->die_named_namespace;
+ ref = ref->die_parent)
+ ref->die_dup = NULL;
+ }
+ orig_size = size * cnt;
+ /* 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). With DW_AT_language c++, 22. */
+ size_t pu_size
+ = (/* CU Header: unit length (initial length).
+ 32-bit DWARF: 4 bytes, 64-bit DWARF: 12 bytes. */
+ 4
+ /* CU Header: version (uhalf).
+ 2 bytes. */
+ + 2
+ /* CU Header: debug_abbrev_offset (section offset).
+ 32-bit DWARF: 4 bytes, 64-bit DWARF: 8 bytes. */
+ + 4
+ /* CU Header: address_size (ubyte).
+ 1 byte. */
+ + 1
+ /* DWARF5 CU header: unit_type (ubyte). */
+ + (die_cu (arr[i])->cu_version >= 5 ? 1 : 0)
+ /* CU Root DIE: abbreviation code (unsigned LEB128).
+ 1 or more bytes. Optimistically assume 1. */
+ + 1
+ /* CU Root DIE: DW_AT_stmt_list (lineptr).
+ 32-bit DWARF: 4 bytes, 64-bit DWARF: 8 bytes. */
+ + 4
+ /* CU Root DIE: DW_AT_comp_dir (string).
+ DW_FORM_strp: 32-bit DWARF: 4 bytes, 64-bit DWARF: 8 bytes.
+ DW_FORM_string: 1 or more bytes.
+ Assume 4 bytes. */
+ + 4
+ /* CU Root DIE: DW_AT_language (constant).
+ 1 or 2 bytes. */
+ + ((uni_lang_p || part_lang)
+ ? nr_bytes_for (die_cu (arr[i])->lang)
+ : 0)
+ /* CU root DIE children terminator: abbreviation code 0
+ (unsigned LEB128).
+ 1 byte. */
+ + 1);
+ /* DW_TAG_imported_unit with DW_AT_import attribute
+ (5 or 9 bytes (the latter for DWARF2 and ptr_size 8)). */
+ size_t import_size
+ = (die_cu (arr[i])->cu_version == 2 ? 1 + ptr_size : 5);
+ /* 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. */
+ size_t namespace_size = 10;
+ new_size = (/* Size of DIEs. */
+ size
+ /* Size of PU. */
+ + pu_size
+ /* Size of imports. */
+ + (part_lang != 0 ? 0 : import_size * cnt)
+ /* Size of namespace DIEs. */
+ + namespace_size * namespaces);
+ if (!second_phase)
+ force = ((deduplication_mode == dm_inter_cu)
+ && (ignore_size || orig_size > new_size));
+ if (force)
+ {
+ dw_die_ref die, *diep;
+ dw_cu_ref refcu = die_cu (arr[i]);
+ dw_cu_ref partial_cu = pool_alloc (dw_cu, sizeof (struct dw_cu));
+ memset (partial_cu, '\0', sizeof (*partial_cu));
+ if (stats_p)
+ {
+ if (!second_phase)
+ stats->pu_ph1_cnt++;
+ else
+ stats->pu_ph2_cnt++;
+ }
+ partial_cu->cu_kind = CU_PU;
+ partial_cu->cu_offset = *last_partial_cu == NULL
+ ? 0 : (*last_partial_cu)->cu_offset + 1;
+ if (dump_pus_p)
+ fprintf (stderr, "Partial unit (%s) @ 0x%x:\n",
+ second_phase ? "phase two" : "phase one",
+ partial_cu->cu_offset);
+ partial_cu->cu_version = refcu->cu_version;
+ if (uni_lang_p)
+ partial_cu->lang = refcu->lang;
+ 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 = pool_alloc (dw_die, sizeof (struct dw_die));
+ memset (die, '\0', sizeof (*die));
+ die->die_toplevel = 1;
+ partial_cu->cu_die = die;
+ die->die_tag = DW_TAG_partial_unit;
+ die->die_offset = -1U;
+ die->die_root = 1;
+ die->die_parent = (dw_die_ref) partial_cu;
+ die->die_nextdup = refcu->cu_die;
+ die->die_size = 9;
+ diep = &die->die_child;
+ for (k = i; k < j; k++)
+ {
+ dw_die_ref child;
+ if (second_phase && !arr[k]->die_ref_seen)
+ continue;
+ if (dump_pus_p)
+ dump_die (arr[k]);
+ child = copy_die_tree (die, arr[k]);
+ if (stats_p)
+ stats->pu_toplevel_die_cnt++;
+ for (ref = arr[k]->die_nextdup; ref; ref = ref->die_nextdup)
+ ref->die_dup = child;
+ if (unlikely (verify_dups_p))
+ verify_dups (child, odr_mode == ODR_BASIC);
+ if (part_lang != 0)
+ {
+ die->die_tag = DW_TAG_compile_unit;
+ partial_cu->lang = part_lang;
+ }
+ if (namespaces)
+ {
+ for (ref = arr[k]->die_parent;
+ ref->die_named_namespace && ref->die_dup == NULL;
+ ref = ref->die_parent)
+ {
+ dw_die_ref namespc
+ = pool_alloc (dw_die, sizeof (struct dw_die));
+ memset (namespc, '\0', sizeof (struct dw_die));
+ namespc->die_toplevel = 1;
+ namespc->die_tag = ref->die_tag;
+ namespc->die_offset = -1U;
+ namespc->die_nextdup = ref;
+ namespc->die_child = child;
+ namespc->die_parent = die;
+ namespc->die_size = 9;
+ namespc->die_named_namespace = 1;
+ child->die_parent = namespc;
+ ref->die_dup = namespc;
+ child = namespc;
+ }
+ if (ref->die_dup != NULL)
+ {
+ dw_die_ref *diep2;
+ for (diep2 = &ref->die_dup->die_child->die_sib;
+ *diep2; diep2 = &(*diep2)->die_sib)
+ ;
+ *diep2 = child;
+ child->die_parent = ref->die_dup;
+ continue;
+ }
+ }
+ *diep = child;
+ diep = &child->die_sib;
+ }
+ if (namespaces)
+ {
+ for (k = i; k < j; k++)
+ {
+ if (second_phase && !arr[k]->die_ref_seen)
+ continue;
+ for (ref = arr[k]->die_parent;
+ ref->die_named_namespace; ref = ref->die_parent)
+ ref->die_dup = NULL;
+ }
+ }
+ }
+ else if (!second_phase)
+ ret = true;
+ if (second_phase)
+ {
+ dw_die_ref next;
+ for (k = i; k < j; k++)
+ {
+ if (arr[k]->die_dup != NULL)
+ continue;
+ for (ref = arr[k]; ref; ref = next)
+ {
+ dw_cu_ref refcu = die_cu (ref);
+ next = ref->die_nextdup;
+ ref->die_dup = NULL;
+ ref->die_nextdup = NULL;
+ ref->die_remove = 0;
+ /* If there are dups within a single CU
+ (arguably a bug in the DWARF producer),
+ keep them linked together, but don't link
+ DIEs across different CUs. */
+ while (deduplication_mode != dm_none
+ && next && refcu == die_cu (next))
+ {
+ dw_die_ref cur = next;
+ next = cur->die_nextdup;
+ cur->die_dup = ref;
+ cur->die_nextdup = ref->die_nextdup;
+ ref->die_nextdup = cur;
+ }
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+/* Partition the duplicate chains in array ARR with size VEC_SIZE, and store
+ the partitions on obstack ob2, with for each partition two entries:
+ the start and the number of unique reffer CUs. */
+static void
+calculate_partitions (dw_die_ref *arr, size_t vec_size)
+{
+ size_t i, j;
+ for (i = 0; i < vec_size; i = j)
+ {
+ size_t cnt = 0;
+ for (j = i + 1; j < vec_size; j++)
+ {
+ size_t this_cnt;
+ if (!same_ref_cus_p (arr[i], arr[j], &this_cnt))
+ break;
+ cnt = this_cnt;
+ }
+ if (cnt == 0)
+ cnt = cnt_ref_cus (arr[i]);
+ obstack_grow (&ob2, &i, sizeof (size_t));
+ obstack_grow (&ob2, &cnt, sizeof (size_t));
+ }
+
+ /* Add element to mark end of partition list. This allows us to do
+ 'j = partitions[idx + 2]' for all partitions. */
+ obstack_grow (&ob2, &j, sizeof (size_t));
+ size_t zero = 0;
+ obstack_grow (&ob2, &zero, sizeof (size_t));
+}
+
+static inline void FORCE_INLINE
+reset_die_ref_seen (void)
+{
+ dw_die_ref die;
+ dw_cu_ref cu;
+ FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die)
+ die->die_ref_seen = 0;
+}
+
+/* If the duplicate chain DIE consists of a singleton ODR_DEF die merged with
+ the ODR_DECL chain, return the singleton ODR_DEF die. Otherwise, return
+ NULL. */
+static inline dw_die_ref FORCE_INLINE
+merged_singleton (dw_die_ref die)
+{
+ dw_die_ref res = NULL;
+ dw_die_ref d;
+ size_t decl_cnt = 0;
+
+ for (d = die; d; d = d->die_nextdup)
+ switch (die_odr_state (d))
+ {
+ case ODR_DEF:
+ if (res)
+ {
+ if (die_cu (res) == die_cu (d))
+ continue;
+ else
+ return NULL;
+ }
+ else
+ res = d;
+ break;
+ case ODR_DECL:
+ decl_cnt++;
+ break;
+ default:
+ return NULL;
+ }
+
+ if (decl_cnt == 0)
+ return NULL;
+
+ return res;
+}
+
+/* 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)
+{
+ dw_cu_ref cu, first_partial_cu = NULL, last_partial_cu = NULL;
+ size_t vec_size, i;
+ unsigned char *to_free;
+ dw_die_ref *arr;
+
+ if (unlikely (fi_multifile))
+ return 0;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "partition_dups\n");
+ }
+
+ to_free = obstack_alloc (&ob2, 1);
+
+ if (odr_active_p)
+ odr_phase = 2;
+
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ partition_find_dups (&ob2, cu->cu_die);
+ vec_size = obstack_object_size (&ob2) / sizeof (void *);
+
+ if (odr_active_p)
+ {
+ arr = (dw_die_ref *) obstack_base (&ob2);
+ if (progress_p)
+ {
+ report_progress ();
+ fprintf (stderr, "partition_dups split_dups\n");
+ }
+ for (i = 0; i < vec_size; i++)
+ {
+ dw_die_ref die = arr[i];
+ if (die_odr_state (die) == ODR_NONE)
+ continue;
+ die = split_dups (die, &ob2);
+ assert (die != NULL);
+ if (unlikely (verify_dups_p))
+ verify_dups (die, true);
+ arr = (dw_die_ref *) obstack_base (&ob2);
+ arr[i] = die;
+ }
+
+ vec_size = obstack_object_size (&ob2) / sizeof (void *);
+
+ reset_die_ref_seen ();
+ for (i = 0; i < vec_size; i++)
+ {
+ dw_die_ref die = arr[i];
+ if (die->die_dup == NULL
+ && die->die_nextdup == NULL)
+ die->die_ref_seen = 1;
+ }
+ for (i = 0; i < vec_size; i++)
+ {
+ dw_die_ref die = arr[i];
+ if (die->die_dup == NULL
+ && die->die_nextdup == NULL)
+ mark_singletons (die_cu (die), die, die, &ob2);
+ else if (odr_mode != ODR_BASIC
+ && die_odr_state (die) != ODR_NONE)
+ {
+ dw_die_ref s = merged_singleton (die);
+ if (s)
+ mark_singletons (die_cu (s), s, s, &ob2);
+ }
+ else if (cnt_ref_cus (die) == 1)
+ mark_singletons (die_cu (die), die, die, &ob2);
+
+ arr = (dw_die_ref *) obstack_base (&ob2);
+ }
+
+ vec_size = obstack_object_size (&ob2) / sizeof (void *);
+ }
+
+ if (odr_active_p)
+ odr_phase = 3;
+
+ if (stats_p)
+ print_dups_stats ();
+
+ if (vec_size != 0)
+ {
+ arr = (dw_die_ref *) obstack_finish (&ob2);
+ if (odr_active_p)
+ for (i = 0; i < vec_size; ++i)
+ {
+ assert (arr[i] != NULL);
+ if (unlikely (verify_dups_p))
+ verify_dups (arr[i], true);
+ }
+ if (dump_dups_p)
+ {
+ for (i = 0; i < vec_size; ++i)
+ {
+ fprintf (stderr, "duplicate chain:\n");
+ dump_dups (arr[i]);
+ }
+ }
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "partition_dups qsort\n");
+ }
+ qsort (arr, vec_size, sizeof (dw_die_ref), partition_cmp);
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "partition_dups after qsort\n");
+ }
+
+ size_t *partitions = (size_t *) obstack_base (&ob2);
+ calculate_partitions (arr, vec_size);
+ size_t nr_partitions
+ = (obstack_object_size (&ob2) / sizeof (size_t)) / 2 - 1;
+ partitions = (size_t *) obstack_finish (&ob2);
+ if (stats_p)
+ stats->part_cnt += nr_partitions;
+
+ if (odr_active_p && odr_mode != ODR_BASIC)
+ for (i = 0; i < vec_size; ++i)
+ arr[i] = reorder_dups (arr[i]);
+ if (partition_dups_1 (arr, nr_partitions, partitions, &first_partial_cu,
+ &last_partial_cu, false))
+ {
+ for (i = 0; i < vec_size; i++)
+ arr[i]->die_ref_seen = arr[i]->die_dup != NULL;
+ for (i = 0; i < vec_size; i++)
+ if (arr[i]->die_dup != NULL)
+ mark_refs (die_cu (arr[i]), arr[i], arr[i],
+ MARK_REFS_FOLLOW_DUPS);
+ partition_dups_1 (arr, nr_partitions, partitions, &first_partial_cu,
+ &last_partial_cu, true);
+ for (i = 0; i < vec_size; i++)
+ arr[i]->die_ref_seen = 0;
+ }
+ }
+ if (first_partial_cu)
+ {
+ last_partial_cu->cu_next = first_cu;
+ first_cu = first_partial_cu;
+ }
+ obstack_free (&ob2, to_free);
+
+ if (stats_p)
+ print_part_stats ();
+
+ 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. */
+ dw_cu_ref cu;
+ /* Linked list of incoming resp. outgoing edges. */
+ struct import_edge *incoming, *outgoing;
+ /* The tail of the linked list of incoming edges. */
+ struct import_edge *incoming_tail;
+ /* 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;
+};
+
+/* 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;
+
+/* Prepare edge E to add to edge_freelist. */
+static inline void FORCE_INLINE
+prepare_free_edge (struct import_edge *e UNUSED)
+{
+#if DEVEL
+ e->icu = (void *)(uintptr_t)-1;
+#endif
+}
+
+/* Add edge E to edge_freelist. */
+static inline void FORCE_INLINE
+free_edge (struct import_edge *e)
+{
+ prepare_free_edge (e);
+ e->next = edge_freelist;
+ edge_freelist = e;
+}
+
+/* Add edge list starting at HEAD and ending at TAIL to edge_freelist.
+ Assume that prepare_free_edge has been called on all elements. */
+static inline void FORCE_INLINE
+free_edges (struct import_edge *head, struct import_edge *tail)
+{
+#if DEVEL
+ if (verify_edge_freelist)
+ {
+ struct import_edge *e;
+ for (e = head; e; e = e->next)
+ {
+ assert (e->icu == (void *)(uintptr_t)-1);
+ if (e == tail)
+ break;
+ }
+ assert (e != NULL);
+ }
+#endif
+ tail->next = edge_freelist;
+ edge_freelist = head;
+}
+
+/* Detach an edge from edge_freelist, and return it. */
+static inline struct import_edge * FORCE_INLINE
+edge_from_freelist (void)
+{
+#if DEVEL
+ assert (edge_freelist);
+#endif
+ struct import_edge *e = edge_freelist;
+ edge_freelist = edge_freelist->next;
+#if DEVEL
+ e->next = (void *)(uintptr_t)-1;
+#endif
+ return e;
+}
+
+/* Return edge_freelist, and set it to NULL. */
+static inline struct import_edge * FORCE_INLINE
+first_edge_from_freelist (void)
+{
+#if DEVEL
+ assert (edge_freelist);
+#endif
+ struct import_edge *e = edge_freelist;
+#if DEVEL
+ edge_freelist = NULL;
+#endif
+ return e;
+}
+
+/* Set edge_freelist to TAIL->next and return HEAD. Assume HEAD was returned
+ by first_edge_from_freelist, and TAIL is reachable from HEAD. */
+static inline struct import_edge * FORCE_INLINE
+last_edge_from_freelist (struct import_edge *head, struct import_edge *tail)
+{
+#if DEVEL
+ assert (!edge_freelist);
+ if (verify_edge_freelist)
+ {
+ struct import_edge *e;
+ for (e = head; e; e = e->next)
+ {
+ if (e == tail)
+ break;
+ }
+ assert (e != NULL);
+ }
+#endif
+ edge_freelist = tail->next;
+ tail->next = NULL;
+ return head;
+}
+
+/* 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_edge **ep_tail,
+ struct import_cu **cus, unsigned int cucount, bool add)
+{
+ unsigned int i = 0;
+ struct import_edge *e, *efirst = NULL, *prev = NULL;
+ while (*ep)
+ if (i < cucount && (*ep)->icu == cus[i])
+ {
+ e = *ep;
+ *ep = e->next;
+ if (efirst == NULL)
+ efirst = e;
+ else
+ free_edge (e);
+ i++;
+ if (ep_tail && *ep_tail == e)
+ *ep_tail = prev;
+ if (i == cucount && !add)
+ return NULL;
+ }
+ else
+ {
+ if (ep_tail)
+ prev = *ep;
+ ep = &(*ep)->next;
+ }
+ assert (i == cucount);
+ *ep = efirst;
+ efirst->next = NULL;
+ if (ep_tail)
+ *ep_tail = efirst;
+ return efirst;
+}
+
+static void
+dump_edges_1 (struct import_cu *ipu)
+{
+ fprintf (stderr, "idx: %u\n", ipu->idx);
+ fprintf (stderr, "cu: 0x%x\n", ipu->cu->cu_offset);
+ struct import_edge *e1;
+ for (e1 = ipu->incoming; e1; e1 = e1->next)
+ fprintf (stderr, "incoming: %u\n", e1->icu->idx);
+ for (e1 = ipu->outgoing; e1; e1 = e1->next)
+ fprintf (stderr, "outgoing: %u\n", e1->icu->idx);
+}
+
+static void
+dump_edges (const char *msg, struct import_cu **ipus, unsigned int npus,
+ unsigned int ncus)
+{
+ struct import_cu *ipu;
+ unsigned int i;
+ fprintf (stderr, "PRINT_EDGES: %s\n", msg);
+ fprintf (stderr, "PUs\n");
+ for (ipu = ipus[0]; ipu; ipu = ipu->next)
+ dump_edges_1 (ipu);
+ fprintf (stderr, "CUs\n");
+ for (i = 0; i < ncus; i++)
+ dump_edges_1 (ipus[i + npus]);
+}
+
+/* Enumerate the different kinds of nodes in the import_cu/import_edge
+ graph. */
+enum node_kind { NODE_CU, NODE_PU_INITIAL, NODE_PU_NEW };
+
+/* Return the node kind for node IDX, given that:
+ - [0, NPUS - 1] are initial PUs,
+ - [NPUS, NPUS + NCUS - 1] are CUs, and
+ - [NPUS + NCUS, ] are new PUs. */
+static enum node_kind
+get_node_kind (unsigned int idx, unsigned int npus, unsigned int ncus)
+{
+ if (idx < npus)
+ return NODE_PU_INITIAL;
+ if (idx < npus + ncus)
+ return NODE_CU;
+ return NODE_PU_NEW;
+}
+
+/* Verify an edge from SRC to DEST during create_import_tree phase PHASE. */
+static void
+verify_edge (enum node_kind src, enum node_kind dest, unsigned int phase)
+{
+ if (phase == 1)
+ {
+ assert (src == NODE_CU && dest == NODE_PU_INITIAL);
+ return;
+ }
+
+ assert (IMPLIES (src == NODE_CU, dest != NODE_CU));
+
+ if (phase == 2)
+ {
+ assert (IMPLIES (src == NODE_PU_NEW, dest == NODE_PU_INITIAL));
+ assert (src != NODE_PU_INITIAL);
+ }
+ else
+ assert (IMPLIES (src == NODE_PU_NEW, dest != NODE_CU));
+}
+
+/* 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,
+ enum node_kind kind, unsigned int npus, unsigned int ncus,
+ unsigned int phase)
+{
+ struct import_edge *e1, *e2;
+ unsigned int last_idx, count;
+ enum node_kind kind2;
+
+ for (last_idx = 0, count = 0, e1 = ipu->incoming;
+ e1;
+ last_idx = e1->icu->idx, count++, e1 = e1->next)
+ {
+ /* Verify that incoming edges are in ascending idx order. */
+ assert (count == 0 || e1->icu->idx > last_idx);
+
+ /* Verify that each incoming edge has a corresponding outgoing edge. */
+ for (e2 = e1->icu->outgoing; e2; e2 = e2->next)
+ if (e2->icu == ipu)
+ break;
+ assert (e2);
+
+ kind2 = get_node_kind (e1->icu->idx, npus, ncus);
+ verify_edge (kind2, kind, phase);
+
+ if (count == ipu->incoming_count - 1)
+ assert (ipu->incoming_tail == e1);
+ }
+
+ /* Verify the number of incoming edges. */
+ assert (ipu->incoming_count == count);
+
+ for (last_idx = 0, count = 0, e1 = ipu->outgoing;
+ e1;
+ last_idx = e1->icu->idx, count++, e1 = e1->next)
+ {
+ /* Verify that outgoing edges are in ascending idx order. */
+ assert (count == 0 || e1->icu->idx > last_idx);
+
+ /* Verify that each outgoing edge has a corresponding incoming edge. */
+ for (e2 = e1->icu->incoming; e2; e2 = e2->next)
+ if (e2->icu == ipu)
+ break;
+ assert (e2);
+
+ kind2 = get_node_kind (e1->icu->idx, npus, ncus);
+ verify_edge (kind, kind2, phase);
+ }
+
+ /* Verify the number of outgoing edges. */
+ 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. */
+static void
+verify_edges (struct import_cu **ipus, unsigned int npus, unsigned int ncus,
+ unsigned int phase)
+{
+ struct import_cu *ipu;
+ unsigned int i, ic, oc;
+
+ ic = 0;
+ oc = 0;
+
+ /* Verify initial PUs. */
+ ipu = NULL;
+ for (i = 0; i < npus; ++i)
+ {
+ ipu = ipus[i];
+ assert (ipu->cu != NULL);
+ if (i < npus - 1)
+ assert (ipu->next == ipus[i + 1]);
+ assert (ipu->incoming != NULL);
+ if (phase <= 2)
+ assert (ipu->outgoing == NULL);
+ verify_edges_1 (ipu, &ic, &oc, NODE_PU_INITIAL, npus, ncus, phase);
+ }
+
+ /* Verify new PUs. */
+ assert (ipu != NULL);
+ for (ipu = ipu->next; ipu; ipu = ipu->next)
+ {
+ assert (phase != 1);
+ assert (ipu->cu == NULL);
+ assert (ipu->incoming != NULL);
+ assert (ipu->outgoing != NULL);
+ verify_edges_1 (ipu, &ic, &oc, NODE_PU_NEW, npus, ncus, phase);
+ }
+
+ /* Verify CUs. */
+ for (i = 0; i < ncus; i++)
+ {
+ ipu = ipus[npus + i];
+ assert (ipu->cu != NULL);
+ assert (ipu->next == NULL);
+ assert (ipu->incoming == NULL);
+ assert (ipu->outgoing != NULL);
+ verify_edges_1 (ipu, &ic, &oc, NODE_CU, npus, ncus, phase);
+ }
+
+ /* Verify that the overall number of incoming and outgoing edges is
+ equal. */
+ assert (ic == oc);
+}
+
+#define BITVECTOR_TYPE unsigned int
+
+/* Return a bitvector containing NBITS bits. */
+static inline BITVECTOR_TYPE *
+bitvector_alloc (unsigned nbits)
+{
+ size_t nbytes = (nbits / 8) + 1;
+ size_t size = nbytes + sizeof (BITVECTOR_TYPE);
+ BITVECTOR_TYPE *res = (BITVECTOR_TYPE *)malloc (size);
+ if (res == NULL)
+ dwz_oom ();
+ memset (res, 0, size);
+ return res;
+}
+
+/* Set bit IDX in bitvector VECTOR. */
+static inline void FORCE_INLINE
+bitvector_set_bit (BITVECTOR_TYPE *vector, unsigned idx)
+{
+ unsigned div = idx / (sizeof (BITVECTOR_TYPE) * 8);
+ unsigned mod = idx % (sizeof (BITVECTOR_TYPE) * 8);
+ vector[div] |= (1U << mod);
+}
+
+/* Test bit IDX in bitvector VECTOR. */
+static inline bool FORCE_INLINE
+bitvector_bit_p (BITVECTOR_TYPE *vector, unsigned idx)
+{
+ unsigned div = idx / (sizeof (BITVECTOR_TYPE) * 8);
+ unsigned mod = idx % (sizeof (BITVECTOR_TYPE) * 8);
+ return (vector[div] & (1U << mod)) != 0;
+}
+
+/* Clear at least bits [A, B] in VECTOR, possibly more. */
+static inline void FORCE_INLINE
+bitvector_clear_bits (BITVECTOR_TYPE *vector, unsigned int a, unsigned int b)
+{
+ unsigned int range_min = a / (sizeof (BITVECTOR_TYPE) * 8);
+ unsigned int range_max = b / (sizeof (BITVECTOR_TYPE) * 8);
+ memset (&vector[range_min], 0,
+ (range_max - range_min + 1) * sizeof (BITVECTOR_TYPE));
+}
+
+/* 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)
+{
+ dw_cu_ref pu, cu, last_partial_cu = NULL;
+ 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;
+ unsigned char *to_free;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "create_import_tree phase 1\n");
+ }
+
+ /* 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/14 (11 CU header + 1 byte
+ abbrev number + 1 byte child end + 1 byte for DWARF5 unit_type). */
+ 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 || (fi_multifile ? alt_first_cu == NULL
+ : first_cu->cu_kind != CU_PU))
+ return 0;
+
+ edge_freelist = NULL;
+ to_free = obstack_alloc (&ob2, 1);
+ min_cu_version = first_cu->cu_version;
+ /* First construct a bipartite graph between CUs and PUs. */
+ for (pu = fi_multifile ? alt_first_cu : first_cu, npus = 0;
+ pu && pu->cu_kind != CU_NORMAL; pu = pu->cu_next)
+ {
+ dw_die_ref die, rdie;
+ dw_cu_ref prev_cu;
+
+ if (pu->cu_die->die_tag == DW_TAG_compile_unit)
+ continue;
+
+ last_partial_cu = pu;
+ for (rdie = pu->cu_die->die_child;
+ rdie->die_named_namespace; rdie = rdie->die_child)
+ ;
+ if (unlikely (fi_multifile) && rdie->die_nextdup == NULL)
+ {
+ pu->u1.cu_icu = NULL;
+ continue;
+ }
+ npus++;
+ 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 (&ob2, sizeof (*ipu));
+ memset (ipu, 0, sizeof (*ipu));
+ ipu->cu = pu;
+ pu->u1.cu_icu = ipu;
+ assert (rdie->die_toplevel);
+ dw_die_ref firstdie = NULL;
+ dw_cu_ref firstdiecu = NULL;
+ for (die = rdie->die_nextdup, prev_cu = NULL;
+ die; die = die->die_nextdup)
+ {
+ dw_cu_ref diecu = die_cu (die);
+ if (firstdie == NULL)
+ {
+ firstdie = die;
+ firstdiecu = die_cu (firstdie);
+ }
+ if (diecu == prev_cu || (die != firstdie && diecu == firstdiecu))
+ continue;
+ ipu->incoming_count++;
+ size += 1 + (diecu->cu_version == 2 ? ptr_size : 4);
+ prev_cu = diecu;
+ }
+ ipu->incoming = (struct import_edge *)
+ obstack_alloc (&ob2,
+ ipu->incoming_count
+ * sizeof (*ipu->incoming));
+ firstdie = NULL;
+ firstdiecu = NULL;
+ for (die = rdie->die_nextdup, i = 0, prev_cu = NULL;
+ die; die = die->die_nextdup)
+ {
+ dw_cu_ref diecu = die_cu (die);
+ if (firstdie == NULL)
+ {
+ firstdie = die;
+ firstdiecu = die_cu (firstdie);
+ }
+ if (diecu == prev_cu || (die != firstdie && diecu == firstdiecu))
+ continue;
+ icu = diecu->u1.cu_icu;
+ if (icu == NULL)
+ {
+ icu = (struct import_cu *)
+ obstack_alloc (&ob2, sizeof (*ipu));
+ memset (icu, 0, sizeof (*icu));
+ icu->cu = diecu;
+ diecu->u1.cu_icu = icu;
+ }
+ ipu->incoming[i++].icu = icu;
+ icu->outgoing_count++;
+ prev_cu = diecu;
+ }
+ ipu->incoming_tail = &ipu->incoming[ipu->incoming_count - 1];
+ }
+ if (npus == 0)
+ {
+ obstack_free (&ob2, to_free);
+ return 0;
+ }
+ for (cu = fi_multifile ? first_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 (&ob2,
+ 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 = fi_multifile ? alt_first_cu : first_cu;
+ pu && pu->cu_kind != CU_NORMAL; pu = pu->cu_next)
+ {
+ ipu = pu->u1.cu_icu;
+ if (ipu == NULL)
+ continue;
+ 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 (&ob2, (npus + ncus) * sizeof (*ipus));
+ for (pu = fi_multifile ? alt_first_cu : first_cu, npus = 0;
+ pu && pu->cu_kind != CU_NORMAL; pu = pu->cu_next)
+ {
+ ipu = pu->u1.cu_icu;
+ if (ipu == NULL)
+ continue;
+ 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 = fi_multifile ? first_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];
+ if (unlikely (dump_edges_p))
+ dump_edges ("phase 1", ipus, npus, ncus);
+ if (unlikely (verify_edges_p))
+ verify_edges (ipus, npus, ncus, 1);
+ if (!import_opt_p)
+ goto opt_done;
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "create_import_tree phase 2\n");
+ }
+ /* 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.
+
+ The example below demonstrates the type of transformation. The
+ transformation is an optimization if the benefit of reducing the number
+ of imports (in other words, edges) is bigger than the cost of adding an
+ extra PU. OTOH, the transformation can be done in the presence of
+ additional incoming edges for PU_3 and PU_4.
+
+ Before: After:
+
+ CU_1---------->PU_3 CU_1 PU_3
+ \ ^ ^ \ ^
+ \ / / \ /
+ \ / / \ /
+ x----o / \ /
+ / \ / \ /
+ / \ / \ /
+ / \ / v /
+ CU_2 x CU_2----->PU_5
+ \ / \ ^ \
+ \ / \ / \
+ \ / \ / \
+ x----o \ / \
+ / \ \ / \
+ / \ \ / \
+ / v v / v
+ CU_3---------->PU_4 CU_3 PU_4
+ */
+ 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)
+ {
+ unsigned int header_size;
+ pusrc[srccount] = e3->icu;
+ header_size = (pusrc[srccount]->cu->cu_version >= 5
+ ? 14 : 13); /* DWARF5 unit_type byte. */
+ cost += edge_cost;
+ if (!edge_cost)
+ cost += pusrc[srccount]->cu->cu_version == 2
+ ? 1 + ptr_size : 5;
+ srccount++;
+ if (ignore_size || ((dstcount - 1) * cost
+ > (header_size
+ + 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 (&ob2, 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, NULL,
+ pudst, dstcount, true)->icu
+ = npu;
+ pusrc[j]->outgoing_count -= dstcount - 1;
+ }
+ for (j = 0; j < dstcount; j++)
+ {
+ remove_import_edges (&pudst[j]->incoming,
+ &pudst[j]->incoming_tail,
+ pusrc, srccount, true)->icu
+ = npu;
+ pudst[j]->incoming_count -= srccount - 1;
+ }
+ npu->incoming = first_edge_from_freelist ();
+ for (j = 0, e4 = npu->incoming; j < srccount; j++)
+ {
+ e4->icu = pusrc[j];
+ if (j == srccount - 1)
+ {
+ npu->incoming
+ = last_edge_from_freelist (npu->incoming,
+ e4);
+ npu->incoming_tail = e4;
+ ep = &e4->next;
+ }
+ else
+ e4 = e4->next;
+ }
+ npu->outgoing = first_edge_from_freelist ();
+ for (j = 0, e4 = npu->outgoing; j < dstcount; j++)
+ {
+ e4->icu = pudst[j];
+ if (j == dstcount - 1)
+ npu->outgoing
+ = last_edge_from_freelist (npu->outgoing, e4);
+ 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, NULL,
+ pudst, dstcount, true)->icu = npu;
+ pusrc[srccount]->outgoing_count -= dstcount - 1;
+ for (j = 0; j < dstcount; j++)
+ {
+ remove_import_edges (&pudst[j]->incoming,
+ &pudst[j]->incoming_tail,
+ pusrc + srccount, 1, false);
+ pudst[j]->incoming_count--;
+ }
+ *ep = edge_from_freelist ();
+ npu->incoming_count++;
+ (*ep)->icu = pusrc[srccount];
+ (*ep)->next = NULL;
+ npu->incoming_tail = *ep;
+ ep = &(*ep)->next;
+ size -= (dstcount - 1) * cost;
+ }
+ }
+ }
+ }
+ }
+ if (unlikely (dump_edges_p))
+ dump_edges ("phase 2", ipus, npus, ncus);
+ if (unlikely (verify_edges_p))
+ verify_edges (ipus, npus, ncus, 2);
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "create_import_tree phase 3\n");
+ }
+ /* Try to merge PUs which have the same set of referrers if
+ beneficial.
+
+ The example below demonstrates the type of transformation. The
+ transformation is an optimization because it reduces the number of import
+ statements (in other words, edges) as well as the number of PUs. It can
+ however not be done if PU_3 or PU_4 have additional incoming edges.
+
+ Before: After:
+
+ CU_1----->PU_3 CU_1
+ \ ^ \
+ \ / \
+ \ / v
+ x PU_3_4
+ / \ ^
+ / \ /
+ / v /
+ CU_2----->PU_4 CU_2
+
+ 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.
+
+ The example below demonstrates the type of transformation. The
+ transformation is an optimization because it reduces the number of import
+ statements (in other words, edges). It can however not be done if PU_3
+ has additional incoming edges.
+
+ Before: After:
+
+ CU_1----->PU_3 CU_1------>PU_3
+ \ ^ ^ |
+ \ / / |
+ \ / / |
+ x / |
+ / \ / |
+ / \ / |
+ / \ / |
+ CU_2 \ CU_2 o
+ \ \ |
+ \ o |
+ \ | |
+ \ | |
+ \ | |
+ \ | |
+ v v v
+ CU_3----->PU_4 CU_3------>PU_4
+ */
+ /* Flag used during PU merging, set for PUs already considered
+ for merging for the given first PU. */
+ BITVECTOR_TYPE *seen = bitvector_alloc (puidx);
+ unsigned int min_seen = UINT_MAX;
+ unsigned int max_seen = 0;
+ for (ipu = ipus[0]; ipu; ipu = ipu->next)
+ {
+ struct import_edge *e1, *e2, *e3, *e4, **e1p, **ep, *prev;
+ 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 || bitvector_bit_p (seen, ipu2->idx))
+ continue;
+ bitvector_set_bit (seen, ipu2->idx);
+ min_seen = MIN (min_seen, ipu2->idx);
+ max_seen = MAX (max_seen, ipu2->idx);
+ maybe_subset = (e1 == ipu->incoming
+ && ipu->incoming_count <= ipu2->incoming_count);
+ maybe_superset = ipu->incoming_count >= ipu2->incoming_count;
+ if (maybe_superset)
+ {
+ /* If the referrer nodes of ipu are a superset of the
+ referrer nodes of ipu2, then ipu's last referrer node
+ should have index larger or equal to the last referrer
+ node of ipu2. */
+ maybe_superset
+ = (ipu->incoming_tail->icu->idx
+ >= ipu2->incoming_tail->icu->idx);
+ }
+ if (maybe_subset)
+ {
+ /* If the referrer nodes of ipu are a subset of the
+ referrer nodes of ipu2, then ipu's last referrer node
+ should have index smaller or equal to the last referrer
+ node of ipu2. */
+ maybe_subset
+ = (ipu->incoming_tail->icu->idx
+ <= ipu2->incoming_tail->icu->idx);
+ }
+ 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 (unlikely (fi_multifile) && ipu2->idx < npus + ncus)
+ continue;
+ if (odr_active_p && odr_mode != ODR_BASIC
+ && ipu2->idx < npus + ncus)
+ continue;
+ /* 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 (!ignore_size || size_dec > size_inc)
+ {
+ struct import_cu **ipup;
+ for (e4 = ipu2->incoming, e3 = NULL; e4; e4 = e4->next)
+ {
+ remove_import_edges (&e4->icu->outgoing, NULL, &ipu2,
+ 1, false);
+ e4->icu->outgoing_count--;
+ prepare_free_edge (e4);
+ e3 = e4;
+ }
+ free_edges (ipu2->incoming, e3);
+ 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;
+ while (e4->icu->incoming_tail->next != NULL)
+ e4->icu->incoming_tail
+ = e4->icu->incoming_tail->next;
+ }
+ }
+ 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
+ && (!fi_multifile || ipusub->idx >= npus + ncus))
+ {
+ for (e3 = ipusub->incoming, ep = &ipusup->incoming,
+ prev = NULL;
+ e3; e3 = e3->next)
+ {
+ remove_import_edges (&e3->icu->outgoing, NULL, &ipusup, 1,
+ false);
+ e3->icu->outgoing_count--;
+ while ((*ep)->icu != e3->icu)
+ {
+ prev = *ep;
+ ep = &(*ep)->next;
+ }
+ e4 = *ep;
+ *ep = e4->next;
+ free_edge (e4);
+ if (ipusup->incoming_tail == e4)
+ ipusup->incoming_tail = prev;
+ }
+ for (ep = &ipusub->outgoing; *ep; ep = &(*ep)->next)
+ if ((*ep)->icu->idx >= ipusup->idx)
+ break;
+ assert (*ep == NULL || (*ep)->icu != ipusup);
+ e4 = edge_from_freelist ();
+ 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_from_freelist ();
+ e4->icu = ipusub;
+ e4->next = *ep;
+ *ep = e4;
+ if (ipusup->incoming_tail->next == e4)
+ ipusup->incoming_tail = e4;
+ ipusup->incoming_count -= ipusub->incoming_count - 1;
+ size -= size_dec - size_inc;
+ if (ipusup == ipu)
+ break;
+ }
+ }
+ }
+ if (min_seen <= max_seen)
+ {
+ bitvector_clear_bits (seen, min_seen, max_seen);
+ min_seen = UINT_MAX;
+ max_seen = 0;
+ }
+ }
+ free (seen);
+ if (unlikely (dump_edges_p))
+ dump_edges ("phase 3", ipus, npus, ncus);
+ if (unlikely (verify_edges_p))
+ verify_edges (ipus, npus, ncus, 3);
+ opt_done:
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "create_import_tree phase 4 (create partial units)\n");
+ }
+ /* Create DW_TAG_partial_unit (and containing dw_cu structures). */
+ if (fi_multifile)
+ {
+ cu_off = 0;
+ last_partial_cu = NULL;
+ }
+ else
+ cu_off = last_partial_cu->cu_offset + 1;
+ for (ipu = ipus[npus - 1]->next; ipu; ipu = ipu->next)
+ {
+ dw_die_ref die;
+ dw_cu_ref partial_cu = pool_alloc (dw_cu, sizeof (struct dw_cu));
+ memset (partial_cu, '\0', sizeof (*partial_cu));
+ partial_cu->cu_kind = CU_PU;
+ partial_cu->cu_offset = cu_off++;
+ partial_cu->cu_version = new_pu_version;
+ partial_cu->u1.cu_icu = ipu;
+ if (unlikely (last_partial_cu == NULL))
+ {
+ partial_cu->cu_next = first_cu;
+ first_cu = partial_cu;
+ }
+ else
+ {
+ partial_cu->cu_next = last_partial_cu->cu_next;
+ last_partial_cu->cu_next = partial_cu;
+ }
+ last_partial_cu = partial_cu;
+ die = pool_alloc (dw_die, sizeof (struct dw_die));
+ memset (die, '\0', sizeof (struct dw_die));
+ die->die_toplevel = 1;
+ partial_cu->cu_die = die;
+ die->die_tag = DW_TAG_partial_unit;
+ die->die_offset = -1U;
+ die->die_root = 1;
+ die->die_parent = (dw_die_ref) 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 = pool_alloc (dw_die, sizeof (struct dw_die));
+ memset (die, '\0', sizeof (*die));
+ die->die_toplevel = 1;
+ die->die_tag = DW_TAG_imported_unit;
+ die->die_offset = -1U;
+ die->die_nextdup = e->icu->cu->cu_die;
+ die->die_parent = cu->cu_die;
+ assert (e->icu->cu->cu_die->die_tag == DW_TAG_partial_unit);
+ 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;
+ if (unlikely (fi_multifile))
+ for (cu = alt_first_cu; cu; cu = cu->cu_next)
+ cu->u1.cu_icu = NULL;
+ obstack_free (&ob2, to_free);
+ return 0;
+}
+
+/* Helper function for die_find_dup, when ORIG has collapsed children. */
+static dw_die_ref
+die_find_collapsed_dup (dw_die_ref die, unsigned int *tick)
+{
+ dw_die_ref child, ret;
+
+ for (child = die->die_child; child; child = child->die_sib)
+ if ((*tick)-- == 0)
+ return child;
+ else if (child->die_child == NULL)
+ continue;
+ else if ((ret = die_find_collapsed_dup (child, tick)) != NULL)
+ return ret;
+ (*tick)--;
+ return NULL;
+}
+
+/* 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;
+ if (orig->die_collapsed_children)
+ {
+ dw_die_ref ret;
+ unsigned int tick;
+ if (die->die_collapsed_child)
+ tick = die->die_tag - 1;
+ else
+ tick = die->die_ref_seen - 1;
+ assert (dup->die_collapsed_children == 0
+ && die->die_parent == orig);
+ ret = die_find_collapsed_dup (dup, &tick);
+ assert (die->die_collapsed_child || ret->die_tag == die->die_tag);
+ return ret;
+ }
+ 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;
+}
+
+/* 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;
+}
+
+/* Return number of bytes needed to encode VAL using
+ sleb128. */
+static unsigned int
+size_of_sleb128 (int64_t val)
+{
+ unsigned int size = 0;
+ unsigned char c;
+ do
+ {
+ c = val & 0x7f;
+ val >>= 7;
+ size++;
+ }
+ while ((val != 0 || (c & 0x40) != 0)
+ && (val != -1 || (c & 0x40) == 0));
+ return size;
+}
+
+/* Hash table mapping original file IDs to new ids. */
+static htab_t line_htab;
+/* Current new maximum file ID. */
+static unsigned int max_line_id;
+
+struct line_entry
+{
+ /* File pointer. */
+ struct dw_file *file;
+ /* Precomputed hash value. */
+ unsigned int hash;
+ /* Corresponding new file ID. */
+ unsigned int new_id;
+};
+ALIGN_STRUCT (line_entry)
+
+/* Hash function in line_htab. */
+static hashval_t
+line_hash (const void *p)
+{
+ struct line_entry *s = (struct line_entry *)p;
+
+ return s->hash;
+}
+
+/* Equality function in line_htab. */
+static int
+line_eq (const void *p, const void *q)
+{
+ struct line_entry *s1 = (struct line_entry *)p;
+ struct line_entry *s2 = (struct line_entry *)q;
+
+ if (s1->hash != s2->hash)
+ return 0;
+ if (s1->file == s2->file)
+ return 1;
+ if (strcmp (s1->file->file, s2->file->file) != 0)
+ return 0;
+ if ((s1->file->dir == NULL) ^ (s2->file->dir == NULL))
+ return 0;
+ if (s1->file->dir && strcmp (s1->file->dir, s2->file->dir) != 0)
+ return 0;
+ return s1->file->time == s2->file->time && s1->file->size == s2->file->size;
+}
+
+/* Map original file ID to new file ID. */
+static unsigned int
+line_htab_lookup (dw_cu_ref cu, unsigned int id)
+{
+ void **slot;
+ struct line_entry le;
+ if (id == 0)
+ return 0;
+ assert (id <= cu->cu_nfiles);
+ le.file = &cu->cu_files[id - 1];
+ hash_init_state ();
+ hash_update_state_object (le.file->time);
+ hash_update_state_object (le.file->size);
+ hash_update_state (le.file->file, strlen (le.file->file) + 1);
+ if (le.file->dir)
+ hash_update_state (le.file->dir, strlen (le.file->dir) + 1);
+ if (line_htab == NULL)
+ {
+ line_htab = htab_try_create (50, line_hash, line_eq, NULL);
+ if (line_htab == NULL)
+ dwz_oom ();
+ max_line_id = 1;
+ }
+ le.hash = hash_digest ();
+ slot = htab_find_slot_with_hash (line_htab, &le, le.hash, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot == NULL)
+ {
+ struct line_entry *l = pool_alloc (line_entry, sizeof (*l));
+ l->file = le.file;
+ l->hash = le.hash;
+ l->new_id = max_line_id++;
+ *slot = (void *) l;
+ return l->new_id;
+ }
+ else
+ return ((struct line_entry *) *slot)->new_id;
+}
+
+/* Hash table for finding duplicate .debug_macro opcode sequences.
+ This hash table is used with two different sets of hash/equality
+ callbacks. One is used either within handle_macro function (from within
+ optimize_multifile), or from handle_macro onwards (read_multifile).
+ The second set is used from read_macro onwards during fi_multifile. */
+static htab_t macro_htab;
+
+/* At the end of read_multifile macro_htab is copied to this variable. */
+static htab_t alt_macro_htab;
+
+struct macro_entry
+{
+ /* Start of the sequence. */
+ unsigned char *ptr;
+ /* Precomputed hash value. LSB bit is used for a flag whether
+ a particular .debug_macro sequence is seen more than once. */
+ unsigned int hash;
+ /* And it's length or 0 if non-shareable. */
+ unsigned int len;
+};
+ALIGN_STRUCT (macro_entry)
+
+/* Hash function in macro_htab. */
+static hashval_t
+macro_hash (const void *p)
+{
+ struct macro_entry *m = (struct macro_entry *)p;
+
+ return m->hash & ~1U;
+}
+
+/* Equality function in macro_htab. */
+static int
+macro_eq (const void *p, const void *q)
+{
+ struct macro_entry *m1 = (struct macro_entry *)p;
+ struct macro_entry *m2 = (struct macro_entry *)q;
+ unsigned char *p1, *p2, *s1, op;
+ unsigned int strp1, strp2;
+
+ if (m1->hash != m2->hash || m1->len != m2->len)
+ return 0;
+ if (rd_multifile)
+ return 0;
+
+ s1 = m1->ptr;
+ p2 = m2->ptr;
+ p1 = s1 + 3;
+
+ while (1)
+ {
+ op = read_8 (p1);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (p1);
+ p1 = (unsigned char *) strchr ((char *) p1, '\0') + 1;
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ skip_leb128 (p1);
+ if (memcmp (s1, p2, p1 - s1) != 0)
+ return 0;
+ p2 += p1 - s1;
+ strp1 = read_32 (p1);
+ strp2 = read_32 (p2);
+ if (op_multifile)
+ {
+ if (strcmp ((char *) debug_sections[DEBUG_STR].data + strp1,
+ (char *) debug_sections[DEBUG_STR].data + strp2)
+ != 0)
+ return 0;
+ }
+ else if (lookup_strp_offset (strp2) != strp1)
+ return 0;
+ s1 = p1;
+ break;
+ default:
+ abort ();
+ }
+ }
+ return memcmp (s1, p2, p1 - s1) == 0;
+}
+
+/* Hash function in macro_htab. */
+static hashval_t
+macro_hash2 (const void *p)
+{
+ struct macro_entry *m = (struct macro_entry *)p;
+
+ return m->ptr - debug_sections[DEBUG_MACRO].data;
+}
+
+/* Equality function in macro_htab. */
+static int
+macro_eq2 (const void *p, const void *q)
+{
+ struct macro_entry *m1 = (struct macro_entry *)p;
+ struct macro_entry *m2 = (struct macro_entry *)q;
+ return m1->ptr == m2->ptr;
+}
+
+/* Parse .debug_macro section, either during write_multifile
+ or during fi_multifile phase. During write_multifile it
+ selects potentially shareable .debug_macro sequences and
+ writes them into debug_sections[DEBUG_MACRO].new_data
+ block it allocates. During fi_multifile it populates
+ macro_htab. In both cases it calls note_strp_offset
+ on DW_FORM_strp offsets. */
+static int
+read_macro (DSO *dso)
+{
+ unsigned char *ptr, *endsec, *dst = NULL;
+ unsigned int version, flags, op, strp;
+ struct macro_entry me, *m;
+
+ ptr = debug_sections[DEBUG_MACRO].data;
+ endsec = ptr + debug_sections[DEBUG_MACRO].size;
+ debug_sections[DEBUG_MACRO].new_size = 0;
+ if (!wr_multifile)
+ {
+ macro_htab = htab_try_create (50, macro_hash2, macro_eq2, NULL);
+ if (macro_htab == NULL)
+ dwz_oom ();
+ }
+
+ while (ptr < endsec)
+ {
+ unsigned char *start = ptr, *s = ptr;
+ bool can_share = true;
+ hashval_t hash = 0;
+ unsigned int strp;
+ void **slot;
+
+ if (ptr + 4 > endsec)
+ {
+ error (0, 0, "%s: .debug_macro header too small", dso->filename);
+ return 1;
+ }
+
+ version = read_16 (ptr);
+ bool supported_version_p = version >= 4 && version <= 5;
+ if (!supported_version_p)
+ {
+ error (0, 0, "%s: Unhandled .debug_macro version %d", dso->filename,
+ version);
+ return 1;
+ }
+ flags = read_8 (ptr);
+ if ((flags & ~2U) != 0)
+ {
+ error (0, 0, "%s: Unhandled .debug_macro flags %d", dso->filename,
+ flags);
+ return 1;
+ }
+ if ((flags & 2) != 0)
+ {
+ ptr += 4;
+ can_share = false;
+ }
+ if (fi_multifile && alt_macro_htab == NULL)
+ can_share = false;
+
+ op = -1U;
+ while (ptr < endsec)
+ {
+ op = read_8 (ptr);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (ptr);
+ ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1;
+ break;
+ case DW_MACRO_start_file:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ can_share = false;
+ break;
+ case DW_MACRO_end_file:
+ can_share = false;
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ skip_leb128 (ptr);
+ strp = read_32 (ptr);
+ note_strp_offset (strp);
+ if (wr_multifile)
+ break;
+ if (can_share)
+ hash = iterative_hash (s, ptr - 4 - s, hash);
+ if (can_share)
+ {
+ unsigned char *p = debug_sections[DEBUG_STR].data + strp;
+ unsigned int len = strlen ((char *) p);
+ hash = iterative_hash (p, len, hash);
+ s = ptr;
+ }
+ break;
+ case DW_MACRO_import:
+ ptr += 4;
+ can_share = false;
+ break;
+ default:
+ error (0, 0, "%s: Unhandled .debug_macro opcode 0x%x",
+ dso->filename, op);
+ return 1;
+ }
+ }
+ if (op != 0)
+ {
+ error (0, 0, "%s: .debug_macro section not zero terminated",
+ dso->filename);
+ return 1;
+ }
+ if (wr_multifile)
+ {
+ if (can_share)
+ debug_sections[DEBUG_MACRO].new_size += ptr - start;
+ continue;
+ }
+
+ me.ptr = start;
+ if (can_share)
+ {
+ hash = iterative_hash (s, ptr - s, hash);
+ me.hash = hash & ~1U;
+ me.len = ptr - start;
+ m = (struct macro_entry *)
+ htab_find_with_hash (alt_macro_htab, &me, me.hash);
+ if (m == NULL)
+ can_share = false;
+ else
+ me.hash = m->ptr - alt_data[DEBUG_MACRO];
+ }
+ if (!can_share)
+ {
+ me.len = 0;
+ me.hash = debug_sections[DEBUG_MACRO].new_size;
+ debug_sections[DEBUG_MACRO].new_size += ptr - start;
+ }
+ slot
+ = htab_find_slot_with_hash (macro_htab, &me,
+ me.ptr - debug_sections[DEBUG_MACRO].data,
+ INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ else
+ {
+ assert (*slot == NULL);
+ m = pool_alloc (macro_entry, sizeof (*m));
+ *m = me;
+ *slot = (void *) m;
+ }
+ }
+
+ if (!wr_multifile)
+ return 0;
+
+ debug_sections[DEBUG_MACRO].new_data
+ = (unsigned char *) malloc (debug_sections[DEBUG_MACRO].new_size);
+ if (debug_sections[DEBUG_MACRO].new_data == NULL)
+ dwz_oom ();
+ dst = debug_sections[DEBUG_MACRO].new_data;
+ for (ptr = debug_sections[DEBUG_MACRO].data; ptr < endsec; )
+ {
+ unsigned char *start = ptr;
+ bool can_share = true;
+
+ ptr += 2;
+ flags = read_8 (ptr);
+ if ((flags & 2) != 0)
+ {
+ ptr += 4;
+ can_share = false;
+ }
+
+ while (1)
+ {
+ op = read_8 (ptr);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (ptr);
+ ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1;
+ break;
+ case DW_MACRO_start_file:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ can_share = false;
+ break;
+ case DW_MACRO_end_file:
+ can_share = false;
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ skip_leb128 (ptr);
+ ptr += 4;
+ break;
+ case DW_MACRO_import:
+ ptr += 4;
+ can_share = false;
+ break;
+ default:
+ abort ();
+ }
+ }
+ if (can_share)
+ {
+ ptr = start + 3;
+
+ while (1)
+ {
+ op = read_8 (ptr);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (ptr);
+ ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1;
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ skip_leb128 (ptr);
+ memcpy (dst, start, ptr - start);
+ dst += ptr - start;
+ strp = lookup_strp_offset (read_32 (ptr));
+ write_32 (dst, strp);
+ start = ptr;
+ break;
+ default:
+ abort ();
+ }
+ }
+ memcpy (dst, start, ptr - start);
+ dst += ptr - start;
+ }
+ }
+ assert (dst == debug_sections[DEBUG_MACRO].new_data
+ + debug_sections[DEBUG_MACRO].new_size);
+
+ return 0;
+}
+
+/* Helper function for handle_macro, called through htab_traverse.
+ Write .debug_macro opcode sequence seen by more than one
+ executable or shared library. */
+static int
+optimize_write_macro (void **slot, void *data)
+{
+ struct macro_entry *m = (struct macro_entry *) *slot;
+ unsigned char **pp = (unsigned char **) data;
+ unsigned char *s = m->ptr;
+ unsigned char *p = s + 3, *q, op;
+ unsigned int strp;
+
+ if ((m->hash & 1) == 0)
+ return 1;
+ while (1)
+ {
+ op = read_8 (p);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (p);
+ p = (unsigned char *) strchr ((char *) p, '\0') + 1;
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ skip_leb128 (p);
+ memcpy (*pp, s, p - s);
+ *pp += p - s;
+ strp = read_32 (p);
+ q = *pp;
+ write_32 (q, lookup_strp_offset (strp));
+ *pp += 4;
+ s = p;
+ break;
+ default:
+ abort ();
+ }
+ }
+ memcpy (*pp, s, p - s);
+ *pp += p - s;
+ return 1;
+}
+
+/* Parse .debug_macro section, during optimize_multifile
+ or during read_multifile. It parses .debug_macro written
+ by write_multifile, so it only contains shareable sequences.
+ Find duplicate sequences, during optimize_multifile write them
+ into debug_sections[DEBUG_MACRO].new_data it allocates,
+ during read_multifile just populates macro_htab (soon to be
+ alt_macro_htab). */
+static void
+handle_macro (void)
+{
+ unsigned char *ptr, *endsec, op;
+ unsigned char *to_free = NULL;
+ struct macro_entry me, *m;
+
+ macro_htab = htab_try_create (50, macro_hash, macro_eq, NULL);
+ if (macro_htab == NULL)
+ dwz_oom ();
+
+ endsec = debug_sections[DEBUG_MACRO].data + debug_sections[DEBUG_MACRO].size;
+ if (op_multifile)
+ {
+ debug_sections[DEBUG_MACRO].new_size = 0;
+ to_free = obstack_alloc (&ob, 1);
+ }
+
+ for (ptr = debug_sections[DEBUG_MACRO].data; ptr < endsec; )
+ {
+ unsigned char *start = ptr, *s = ptr, *p;
+ hashval_t hash = 0;
+ unsigned int len;
+ void **slot;
+ bool can_share = true;
+
+ ptr += 3;
+ while (1)
+ {
+ op = read_8 (ptr);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (ptr);
+ ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1;
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ skip_leb128 (ptr);
+ hash = iterative_hash (s, ptr - s, hash);
+ p = debug_sections[DEBUG_STR].data + read_32 (ptr);
+ len = strlen ((char *) p);
+ hash = iterative_hash (p, len, hash);
+ if (op_multifile
+ /* This should only happen if there were multiple
+ same transparent units within a single object file. */
+ && htab_find_with_hash (strp_htab, p,
+ hash (p, len)) == NULL)
+ can_share = false;
+ s = ptr;
+ break;
+ default:
+ abort ();
+ }
+ }
+ if (!can_share)
+ continue;
+ hash = iterative_hash (s, ptr - s, hash);
+ me.ptr = start;
+ me.hash = hash & ~1U;
+ me.len = ptr - start;
+ slot = htab_find_slot_with_hash (macro_htab, &me, me.hash, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ else if (*slot != NULL)
+ {
+ m = (struct macro_entry *) *slot;
+ if (op_multifile && (m->hash & 1) == 0)
+ {
+ m->hash |= 1;
+ debug_sections[DEBUG_MACRO].new_size += me.len;
+ }
+ }
+ else if (op_multifile)
+ {
+ m = (struct macro_entry *) obstack_alloc (&ob, sizeof (*m));
+ *m = me;
+ *slot = (void *) m;
+ }
+ else
+ {
+ m = pool_alloc (macro_entry, sizeof (*m));
+ *m = me;
+ *slot = (void *) m;
+ }
+ }
+
+ if (op_multifile)
+ {
+ if (debug_sections[DEBUG_MACRO].new_size)
+ {
+ unsigned char *p;
+ debug_sections[DEBUG_MACRO].new_data
+ = malloc (debug_sections[DEBUG_MACRO].new_size);
+ p = debug_sections[DEBUG_MACRO].new_data;
+ htab_traverse (macro_htab, optimize_write_macro, &p);
+ assert (p == debug_sections[DEBUG_MACRO].new_data
+ + debug_sections[DEBUG_MACRO].new_size);
+ htab_delete (macro_htab);
+ macro_htab = NULL;
+ }
+ obstack_free (&ob, (void *) to_free);
+ }
+}
+
+/* Write new content of .debug_macro section during fi_multifile phase. */
+static void
+write_macro (void)
+{
+ unsigned char *ptr, *endsec, *dst;
+ unsigned int op, strp;
+ struct macro_entry me, *m;
+
+ endsec = debug_sections[DEBUG_MACRO].data + debug_sections[DEBUG_MACRO].size;
+ debug_sections[DEBUG_MACRO].new_data
+ = (unsigned char *) malloc (debug_sections[DEBUG_MACRO].new_size);
+ if (debug_sections[DEBUG_MACRO].new_data == NULL)
+ dwz_oom ();
+ dst = debug_sections[DEBUG_MACRO].new_data;
+ for (ptr = debug_sections[DEBUG_MACRO].data; ptr < endsec; )
+ {
+ unsigned char *s = ptr;
+ unsigned char flags;
+
+ me.ptr = ptr;
+ m = (struct macro_entry *)
+ htab_find_with_hash (macro_htab, &me,
+ me.ptr - debug_sections[DEBUG_MACRO].data);
+ if (m->len)
+ {
+ ptr += m->len;
+ continue;
+ }
+
+ ptr += 2;
+ flags = read_8 (ptr);
+ if ((flags & 2) != 0)
+ ptr += 4;
+
+ while (1)
+ {
+ op = read_8 (ptr);
+ if (op == 0)
+ break;
+
+ switch (op)
+ {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ skip_leb128 (ptr);
+ ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1;
+ break;
+ case DW_MACRO_start_file:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ break;
+ case DW_MACRO_end_file:
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ memcpy (dst, s, ptr - 1 - s);
+ dst += ptr - 1 - s;
+ s = ptr - 1;
+ skip_leb128 (ptr);
+ strp = read_32 (ptr);
+ switch (note_strp_offset2 (strp))
+ {
+ case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
+ *dst = op == DW_MACRO_define_strp
+ ? DW_MACRO_define_sup
+ : DW_MACRO_undef_sup;
+ dst++;
+ s++;
+ break;
+ default:
+ break;
+ }
+ memcpy (dst, s, ptr - 4 - s);
+ dst += ptr - 4 - s;
+ write_32 (dst, lookup_strp_offset (strp));
+ s = ptr;
+ break;
+ case DW_MACRO_import:
+ memcpy (dst, s, ptr - 1 - s);
+ dst += ptr - 1 - s;
+ me.ptr = debug_sections[DEBUG_MACRO].data + read_32 (ptr);
+ m = (struct macro_entry *)
+ htab_find_with_hash (macro_htab, &me,
+ me.ptr
+ - debug_sections[DEBUG_MACRO].data);
+ if (m->len)
+ *dst = DW_MACRO_import_sup;
+ else
+ *dst = DW_MACRO_import;
+ dst++;
+ write_32 (dst, m->hash);
+ s = ptr;
+ break;
+ default:
+ abort ();
+ }
+ }
+ memcpy (dst, s, ptr - s);
+ dst += ptr - s;
+ }
+ assert (dst == debug_sections[DEBUG_MACRO].new_data
+ + debug_sections[DEBUG_MACRO].new_size);
+}
+
+/* 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_cu_ref cu, dw_die_ref die,
+ dw_cu_ref refcu, dw_die_ref ref,
+ struct abbrev_tag *t, unsigned int *ndies,
+ struct obstack *vec, bool recompute)
+{
+ dw_die_ref child, ref_child, sib = NULL, origin = NULL;
+ unsigned int i, j;
+ uint64_t low_pc = 0;
+ void **slot;
+
+ if (unlikely (recompute) && die->u.p2.die_new_abbrev != NULL)
+ {
+ if (cu->cu_intracu_form == DW_FORM_ref_udata)
+ die->die_ref_seen = 1;
+ else
+ {
+ die->die_size -= size_of_uleb128 (die->u.p2.die_new_abbrev->entry)
+ + die->u.p2.die_intracu_udata_size;
+ die->die_ref_seen = 0;
+ }
+ for (child = die->die_child; child; child = child->die_sib)
+ if (build_abbrevs_for_die (h, cu, child, NULL, NULL, t, ndies, vec,
+ true))
+ return 1;
+ return 0;
+ }
+
+ die->u.p2.die_new_abbrev = NULL;
+ die->u.p2.die_new_offset = 0;
+ die->u.p2.die_intracu_udata_size = 0;
+ die->die_ref_seen = 0;
+
+ if (wr_multifile ? die->die_no_multifile : 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->die_nextdup->die_dup == die)
+ {
+ ref = die->die_nextdup;
+ if (ref != NULL)
+ refcu = die_cu (ref);
+ }
+ if (ref == NULL)
+ origin = die->die_nextdup;
+ }
+ else
+ {
+ ref = die;
+ refcu = cu;
+ if (wr_multifile
+ && (die->die_root || die->die_named_namespace))
+ origin = die;
+ }
+ if (die->die_child && die->die_sib)
+ for (sib = die->die_sib; sib; sib = sib->die_sib)
+ if (wr_multifile ? !sib->die_no_multifile : !sib->die_remove)
+ break;
+ if (ref != NULL && origin == NULL)
+ {
+ unsigned char *base
+ = cu->cu_kind == CU_TYPES
+ ? debug_sections[DEBUG_TYPES].data
+ : debug_sections[DEBUG_INFO].data;
+ unsigned char *ptr = base + ref->die_offset;
+ struct abbrev_tag *reft = ref->die_abbrev;
+
+ skip_leb128 (ptr);
+ /* No longer count the abbrev uleb128 size in die_size.
+ We'll add it back after determining the new abbrevs. */
+ if (unlikely (wr_multifile || op_multifile || fi_multifile)
+ || unlikely (recompute))
+ i = -1U;
+ else
+ 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;
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ if (reft->attr[i].attr == DW_AT_high_pc)
+ i = -2U;
+ break;
+ case DW_FORM_addr:
+ if (reft->attr[i].attr == DW_AT_high_pc
+ && cu->cu_version >= 4)
+ i = -2U;
+ break;
+ default:
+ break;
+ }
+ if (i != -1U)
+ {
+ die->die_size -= ptr - (base + ref->die_offset);
+ /* 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;
+ if (t->attr[i].form == DW_FORM_implicit_const)
+ t->values[i] = reft->values[i];
+ }
+ t->nattr = reft->nattr;
+ }
+ else
+ {
+ die->die_size = 0;
+ /* 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 = 0;
+ unsigned char *orig_ptr = ptr;
+
+ while (form == DW_FORM_indirect)
+ form = read_uleb128 (ptr);
+
+ if (unlikely (wr_multifile || op_multifile)
+ && (reft->attr[i].attr == DW_AT_decl_file
+ || reft->attr[i].attr == DW_AT_call_file))
+ {
+ switch (form)
+ {
+ case DW_FORM_data1: value = read_8 (ptr); break;
+ case DW_FORM_data2: value = read_16 (ptr); break;
+ case DW_FORM_data4: value = read_32 (ptr); break;
+ case DW_FORM_data8: value = read_64 (ptr); break;
+ case DW_FORM_udata: value = read_uleb128 (ptr); break;
+ case DW_FORM_sdata: value = read_sleb128 (ptr); break;
+ case DW_FORM_implicit_const:
+ value = reft->values[i];
+ break;
+ default:
+ error (0, 0, "Unhandled %s for %s",
+ get_DW_FORM_str (form),
+ get_DW_AT_str (reft->attr[i].attr));
+ return 1;
+ }
+ value = line_htab_lookup (refcu, value);
+ if (form != DW_FORM_implicit_const)
+ {
+ if (value <= 0xff)
+ {
+ form = DW_FORM_data1;
+ die->die_size++;
+ }
+ else if (value <= 0xffff)
+ {
+ form = DW_FORM_data2;
+ die->die_size += 2;
+ }
+ else if (value <= 0xffffffff)
+ {
+ form = DW_FORM_data4;
+ die->die_size += 4;
+ }
+ else
+ {
+ form = DW_FORM_data8;
+ die->die_size += 8;
+ }
+ }
+ t->attr[j].attr = reft->attr[i].attr;
+ t->attr[j].form = form;
+ if (form == DW_FORM_implicit_const)
+ t->values[j] = value;
+ j++;
+ continue;
+ }
+
+ if (unlikely (fi_multifile)
+ && (reft->attr[i].attr == DW_AT_GNU_macros
+ || reft->attr[i].attr == DW_AT_macros)
+ && alt_macro_htab != NULL)
+ {
+ struct macro_entry me, *m;
+
+ switch (form)
+ {
+ case DW_FORM_data4:
+ case DW_FORM_sec_offset:
+ value = read_32 (ptr);
+ break;
+ default:
+ error (0, 0, "Unhandled %s for %s",
+ get_DW_FORM_str (form),
+ get_DW_AT_str (reft->attr[i].attr));
+ return 1;
+ }
+ me.ptr = debug_sections[DEBUG_MACRO].data + value;
+ m = (struct macro_entry *)
+ htab_find_with_hash (macro_htab, &me, value);
+ if (m->len)
+ {
+ error (0, 0, "%s referencing transparent include",
+ get_DW_AT_str (reft->attr[i].attr));
+ return 1;
+ }
+ ptr -= 4;
+ }
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ if (unlikely (fi_multifile))
+ {
+ dw_die_ref refdt;
+ value = read_size (ptr,
+ refcu->cu_version == 2
+ ? ptr_size : 4);
+ ptr += refcu->cu_version == 2 ? ptr_size : 4;
+ refd = off_htab_lookup (NULL, value);
+ assert (refd != NULL);
+ refdt = refd;
+ while (refdt->die_toplevel == 0)
+ refdt = refdt->die_parent;
+ if (refdt->die_dup
+ && !refdt->die_op_type_referenced
+ && die_cu (refdt->die_dup)->cu_kind == CU_ALT)
+ {
+ t->attr[j].attr = reft->attr[i].attr;
+ t->attr[j++].form
+ = dwarf_5 ? DW_FORM_ref_sup4 : DW_FORM_GNU_ref_alt;
+ die->die_size += 4;
+ continue;
+ }
+ break;
+ }
+ ptr += refcu->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_addr:
+ ptr += ptr_size;
+ if (reft->attr[i].attr == DW_AT_low_pc
+ && cu->cu_version >= 4)
+ low_pc = read_size (ptr - ptr_size, ptr_size);
+ else if (reft->attr[i].attr == DW_AT_high_pc
+ && low_pc)
+ {
+ uint64_t high_pc = read_size (ptr - ptr_size, ptr_size);
+ /* If both DW_AT_low_pc and DW_AT_high_pc attributes
+ are present and have DW_FORM_addr, attempt to shrink
+ the DIE by using DW_FORM_udata or DW_FORM_data4
+ form for the latter in DWARF4+. Don't try
+ DW_FORM_data[12], that might increase .debug_abbrev
+ size too much or increase the uleb128 size of too
+ many abbrev numbers. */
+ if (high_pc > low_pc)
+ {
+ unsigned int nform = 0;
+ unsigned int sz = size_of_uleb128 (high_pc - low_pc);
+ if (sz <= 4 && sz <= (unsigned) ptr_size)
+ nform = DW_FORM_udata;
+ else if (ptr_size > 4
+ && high_pc - low_pc <= 0xffffffff)
+ {
+ nform = DW_FORM_data4;
+ sz = 4;
+ }
+ else if (sz <= (unsigned) ptr_size)
+ nform = DW_FORM_udata;
+ if (nform)
+ {
+ t->attr[j].attr = reft->attr[i].attr;
+ t->attr[j++].form = nform;
+ die->die_size += sz;
+ continue;
+ }
+ }
+ }
+ break;
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ break;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ ++ptr;
+ break;
+ case DW_FORM_data2:
+ ptr += 2;
+ break;
+ case DW_FORM_data4:
+ if (reft->attr[i].attr == DW_AT_high_pc)
+ {
+ uint32_t range_len = read_32 (ptr);
+ unsigned int sz = size_of_uleb128 (range_len);
+ if (sz <= 4)
+ {
+ t->attr[j].attr = reft->attr[i].attr;
+ t->attr[j++].form = DW_FORM_udata;
+ die->die_size += sz;
+ continue;
+ }
+ break;
+ }
+ ptr += 4;
+ break;
+ case DW_FORM_sec_offset:
+ ptr += 4;
+ break;
+ case DW_FORM_data8:
+ if (reft->attr[i].attr == DW_AT_high_pc)
+ {
+ unsigned int nform = 0;
+ uint64_t range_len = read_64 (ptr);
+ unsigned int sz = size_of_uleb128 (range_len);
+ if (sz <= 4)
+ nform = DW_FORM_udata;
+ else if (range_len <= 0xffffffff)
+ {
+ nform = DW_FORM_data4;
+ sz = 4;
+ }
+ else if (sz <= 8)
+ nform = DW_FORM_udata;
+ if (nform)
+ {
+ t->attr[j].attr = reft->attr[i].attr;
+ t->attr[j++].form = nform;
+ die->die_size += sz;
+ continue;
+ }
+ break;
+ }
+ ptr += 8;
+ break;
+ case DW_FORM_ref_sig8:
+ ptr += 8;
+ break;
+ case DW_FORM_data16:
+ ptr += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ skip_leb128 (ptr);
+ break;
+ case DW_FORM_strp:
+ if (unlikely (op_multifile || fi_multifile))
+ {
+ form = note_strp_offset2 (read_32 (ptr));
+ if (form != DW_FORM_strp)
+ {
+ t->attr[j].attr = reft->attr[i].attr;
+ t->attr[j++].form = form;
+ die->die_size += 4;
+ continue;
+ }
+ }
+ else
+ ptr += 4;
+ break;
+ case DW_FORM_line_strp:
+ /* Since we don't register the line_strp we cannot
+ change the form in the case of multifile. */
+ 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 ();
+ }
+ 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 (refcu, refcu->cu_offset + value);
+ assert (refd != NULL);
+ refdt = refd;
+ while (refdt->die_toplevel == 0)
+ refdt = refdt->die_parent;
+ if (refdt->die_dup && refdt->die_op_type_referenced)
+ {
+ if (cu == die_cu (refdt))
+ form = DW_FORM_ref4;
+ else if (cu == die_cu (refdt->die_dup))
+ {
+ form = DW_FORM_ref4;
+ refd = die_find_dup (refdt, refdt->die_dup,
+ refd);
+ }
+ else
+ form = DW_FORM_ref_addr;
+ }
+ else
+ {
+ if (refdt->die_dup)
+ refd = die_find_dup (refdt, refdt->die_dup, refd);
+ if (cu == die_cu (refd))
+ form = DW_FORM_ref4;
+ else if (die_cu (refd)->cu_kind == CU_ALT)
+ form = (dwarf_5
+ ? DW_FORM_ref_sup4 : DW_FORM_GNU_ref_alt);
+ else
+ form = DW_FORM_ref_addr;
+ }
+ }
+ if (form == DW_FORM_ref_addr)
+ die->die_size += cu->cu_version == 2 ? ptr_size : 4;
+ else if (form == DW_FORM_GNU_ref_alt
+ || form == DW_FORM_ref_sup4)
+ die->die_size += 4;
+ else
+ {
+ if (unlikely (recompute))
+ form = cu->cu_intracu_form;
+ if (likely (!recompute) || form == DW_FORM_ref_udata)
+ {
+ 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;
+ if (reft->attr[i].form == DW_FORM_implicit_const)
+ t->values[j] = reft->values[i];
+ j++;
+ die->die_size += ptr - orig_ptr;
+ }
+ t->nattr = j;
+ }
+ }
+ else
+ switch (die->die_tag)
+ {
+ case DW_TAG_partial_unit:
+ case DW_TAG_compile_unit:
+ t->nattr = 0;
+ die->die_size = 0;
+ if (origin == NULL)
+ break;
+ refcu = die_cu (origin);
+ if (refcu->cu_nfiles)
+ {
+ t->attr[0].attr = DW_AT_stmt_list;
+ t->attr[0].form = cu->cu_version < 4
+ ? DW_FORM_data4 : DW_FORM_sec_offset;
+ die->die_size += 4;
+ t->nattr++;
+ }
+ if (uni_lang_p || cu->cu_die->die_tag == DW_TAG_compile_unit)
+ {
+ unsigned int lang_size = nr_bytes_for (cu->lang);
+ die->die_size += lang_size;
+ t->attr[t->nattr].attr = DW_AT_language;
+ switch (lang_size)
+ {
+ case 1:
+ t->attr[t->nattr].form = DW_FORM_data1;
+ break;
+ case 2:
+ t->attr[t->nattr].form = DW_FORM_data2;
+ break;
+ default:
+ abort ();
+ }
+ t->nattr++;
+ }
+ if (refcu->cu_comp_dir)
+ {
+ enum dwarf_form form;
+ unsigned char *ptr = get_AT (origin, DW_AT_comp_dir, &form);
+ assert (ptr && (form == DW_FORM_string
+ || form == DW_FORM_strp
+ || form == DW_FORM_line_strp));
+ if (form == DW_FORM_strp)
+ {
+ if (unlikely (op_multifile || fi_multifile))
+ form = note_strp_offset2 (read_32 (ptr));
+ die->die_size += 4;
+ }
+ else if (form == DW_FORM_line_strp)
+ die->die_size += 4;
+ else
+ die->die_size
+ += strlen (refcu->cu_comp_dir) + 1;
+ t->attr[t->nattr].attr = DW_AT_comp_dir;
+ t->attr[t->nattr].form = form;
+ t->nattr++;
+ }
+ break;
+ case DW_TAG_namespace:
+ case DW_TAG_module:
+ {
+ enum dwarf_form form;
+ unsigned char *ptr = get_AT (origin, DW_AT_name, &form);
+ assert (ptr && (form == DW_FORM_string
+ || form == DW_FORM_strp
+ || form == DW_FORM_line_strp));
+ if (form == DW_FORM_strp)
+ {
+ if (unlikely (op_multifile || fi_multifile))
+ form = note_strp_offset2 (read_32 (ptr));
+ die->die_size = 4;
+ }
+ else if (form == DW_FORM_line_strp)
+ die->die_size += 4;
+ else
+ die->die_size = strlen ((char *) ptr) + 1;
+ t->attr[0].attr = DW_AT_name;
+ t->attr[0].form = form;
+ 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->nattr = 1;
+ if (die_cu (die->die_nextdup)->cu_kind == CU_ALT)
+ {
+ t->attr[0].form = dwarf_5 ? DW_FORM_ref_sup4 : DW_FORM_GNU_ref_alt;
+ die->die_size = 4;
+ }
+ else
+ {
+ t->attr[0].form = DW_FORM_ref_addr;
+ die->die_size = cu->cu_version == 2 ? ptr_size : 4;
+ }
+ break;
+ default:
+ abort ();
+ }
+ compute_abbrev_hash (t);
+ slot = htab_find_slot_with_hash (h, t, t->hash,
+ recompute ? NO_INSERT : INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (unlikely (recompute))
+ assert (*slot);
+ if (*slot)
+ {
+ if (likely (!recompute))
+ ((struct abbrev_tag *)*slot)->nusers++;
+ die->u.p2.die_new_abbrev = (struct abbrev_tag *)*slot;
+ }
+ else
+ {
+ struct abbrev_tag *newt = pool_clone_abbrev (t);
+ *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, cu, child, refcu, ref_child,
+ t, ndies, vec, recompute))
+ return 1;
+ }
+ else
+ for (child = die->die_child; child; child = child->die_sib)
+ if (build_abbrevs_for_die (h, cu, child, NULL, NULL, t, ndies, vec,
+ recompute))
+ return 1;
+ return 0;
+}
+
+/* Build new abbreviations for CU. T, NDIES and VEC arguments like
+ for build_abbrevs_for_die. */
+static int
+build_abbrevs (dw_cu_ref 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)
+ dwz_oom ();
+
+ if (build_abbrevs_for_die (h, cu, cu->cu_die, NULL, NULL, t, ndies, vec,
+ false))
+ {
+ htab_delete (h);
+ 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 *data)
+{
+ struct obstack *obp = (struct obstack *) data;
+ obstack_ptr_grow (obp, *slot);
+ 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;
+ if (t1->attr[i].form == DW_FORM_implicit_const)
+ {
+ if (t1->values[i] < t2->values[i])
+ return -1;
+ if (t1->values[i] > t2->values[i])
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* 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 (wr_multifile ? die->die_no_multifile : die->die_remove)
+ return off;
+ die->u.p2.die_new_offset = off;
+ if (likely (die->die_ref_seen == 0))
+ {
+ 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)
+ switch (t->attr[i].form)
+ {
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref_udata:
+ die->u.p2.die_intracu_udata_size += intracusize;
+ break;
+ default:
+ break;
+ }
+ 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 (wr_multifile ? die->die_no_multifile : 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 || die->die_ref_seen);
+ 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_cu_ref cu, dw_die_ref die, unsigned int off,
+ unsigned int intracusize, dw_die_ref **intracuvec)
+{
+ dw_die_ref child;
+ unsigned int ref_seen = die->die_ref_seen;
+ if (wr_multifile ? die->die_no_multifile : die->die_remove)
+ return off;
+ die->u.p2.die_new_offset = off;
+ die->die_ref_seen = 0;
+ /* 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
+ && !wr_multifile
+ && size_of_uleb128 (off)
+ > size_of_uleb128 (die->die_offset - 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 || ref_seen);
+ off += die->die_size;
+ for (child = die->die_child; child; child = child->die_sib)
+ {
+ off = finalize_new_die_offsets (cu, 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)
+{
+ dw_cu_ref cu1 = *(dw_cu_ref *)p;
+ dw_cu_ref cu2 = *(dw_cu_ref *)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_kind != CU_PU && cu2->cu_kind == CU_PU)
+ return -1;
+ if (cu1->cu_kind == CU_PU && cu2->cu_kind != CU_PU)
+ 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. */
+static int
+compute_abbrevs (DSO *dso)
+{
+ unsigned long total_size = 0, types_size = 0, abbrev_size = 0;
+ dw_cu_ref cu, *cuarr;
+ struct abbrev_tag *t;
+ unsigned int ncus, nlargeabbrevs = 0, i, laststart;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "compute_abbrevs\n");
+ }
+
+ t = (struct abbrev_tag *)
+ obstack_alloc (&ob2,
+ sizeof (*t)
+ + (max_nattr + 4) * sizeof (struct abbrev_attr)
+ + (max_nattr + 4) * sizeof (int64_t));
+ t->values = (int64_t *) &t->attr[max_nattr + 4];
+ for (cu = first_cu, ncus = 0; cu; cu = cu->cu_next)
+ {
+ 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;
+ unsigned int headersz = (cu->cu_kind == CU_TYPES
+ ? 23 : (cu->cu_version >= 5 ? 12 : 11));
+
+ if (unlikely (fi_multifile) && cu->cu_die->die_remove)
+ continue;
+ if (unlikely (low_mem) && cu->cu_kind != CU_PU)
+ expand_children (cu->cu_die);
+ ncus++;
+ if (build_abbrevs (cu, t, &ndies, &ob2))
+ return 1;
+ nabbrevs = htab_elements (cu->cu_new_abbrev);
+ htab_traverse (cu->cu_new_abbrev, list_abbrevs, &ob);
+ assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *));
+ arr = (struct abbrev_tag **) obstack_finish (&ob);
+ intracu = obstack_object_size (&ob2) / sizeof (void *) / 2;
+ obstack_ptr_grow (&ob2, NULL);
+ intracuarr = (dw_die_ref *) obstack_finish (&ob2);
+ 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 && !wr_multifile)
+ {
+ 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 = headersz + 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);
+ cu->initial_intracusize = intracusize;
+ off = init_new_die_offsets (cu->cu_die, headersz, intracusize);
+ do
+ {
+ intracuvec = intracuarr;
+ i = update_new_die_offsets (cu->cu_die, headersz, &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->cu_die, headersz, 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);
+ return 1;
+ }
+ assert (*intracuvec == NULL && off == cusize);
+ cu->cu_intracu_form = intracuform;
+
+ 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)
+ dwz_oom ();
+ assert (slot != NULL && *slot == NULL);
+ *slot = arr[i];
+ }
+ }
+ obstack_free (&ob, (void *) arr);
+ obstack_free (&ob2, (void *) intracuarr);
+ if (cu->cu_kind == CU_TYPES)
+ {
+ cu->cu_new_offset = types_size;
+ types_size += cusize;
+ }
+ else
+ {
+ cu->cu_new_offset = (wr_multifile ? multi_info_off : 0) + total_size;
+ total_size += cusize;
+ }
+
+ if (unlikely (low_mem) && cu->cu_kind != CU_PU)
+ collapse_children (cu, cu->cu_die);
+ }
+ if (wr_multifile)
+ total_size += 11; /* See the end of write_info. */
+ obstack_free (&ob2, (void *) t);
+ cuarr = (dw_cu_ref *) obstack_alloc (&ob2, ncus * sizeof (dw_cu_ref));
+ for (cu = first_cu, i = 0; cu; cu = cu->cu_next)
+ if (cu->u1.cu_new_abbrev_owner == NULL
+ && (likely (!fi_multifile)
+ || cu->cu_kind != CU_NORMAL
+ || !cu->cu_die->die_remove))
+ cuarr[i++] = cu;
+ assert (i == ncus);
+ qsort (cuarr, ncus, sizeof (dw_cu_ref), 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, &ob2);
+ assert (obstack_object_size (&ob2) == nabbrevs * sizeof (void *));
+ arr = (struct abbrev_tag **) obstack_finish (&ob2);
+ 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);
+ 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)
+ dwz_oom ();
+ if (*slot != NULL)
+ arr[k]->entry = ((struct abbrev_tag *) *slot)->entry;
+ else
+ {
+ struct abbrev_tag *newt;
+ arr[k]->entry = ++entry;
+ newt = pool_clone_abbrev (arr[k]);
+ *slot = newt;
+ }
+ }
+ cuarr[j]->u2.cu_largest_entry = entry;
+ cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j];
+ break;
+ }
+ obstack_free (&ob2, (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;
+ }
+ 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++;
+ }
+ 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)
+ dwz_oom ();
+ if (*slot != NULL)
+ arr[k]->entry = ((struct abbrev_tag *) *slot)->entry;
+ else
+ {
+ struct abbrev_tag *newt;
+ arr[k]->entry = ++entry;
+ newt = pool_clone_abbrev (arr[k]);
+ *slot = newt;
+ }
+ }
+ cuarr[j]->u2.cu_largest_entry = entry;
+ cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j];
+ }
+ }
+ obstack_free (&ob2, (void *) arr);
+ }
+ obstack_free (&ob2, (void *) t);
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ {
+ struct abbrev_tag **arr;
+ unsigned int nabbrevs, j;
+
+ if (unlikely (fi_multifile)
+ && cu->cu_kind == CU_NORMAL
+ && cu->cu_die->die_remove)
+ continue;
+ if (cu->u1.cu_new_abbrev_owner != NULL)
+ {
+ cu->u2.cu_new_abbrev_offset = -1U;
+ if (cu->cu_new_abbrev)
+ htab_delete (cu->cu_new_abbrev);
+ cu->cu_new_abbrev = NULL;
+ continue;
+ }
+ cu->u2.cu_new_abbrev_offset
+ = (wr_multifile ? multi_abbrev_off : 0) + abbrev_size;
+ nabbrevs = htab_elements (cu->cu_new_abbrev);
+ htab_traverse (cu->cu_new_abbrev, list_abbrevs, &ob);
+ 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);
+ if (arr[i]->attr[j].form == DW_FORM_implicit_const)
+ abbrev_size += size_of_sleb128 (arr[i]->values[j]);
+ }
+ abbrev_size += 2;
+ }
+ abbrev_size += 1;
+ obstack_free (&ob, (void *) arr);
+ }
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (unlikely (fi_multifile)
+ && cu->cu_kind == CU_NORMAL
+ && cu->cu_die->die_remove)
+ continue;
+ else if (cu->u2.cu_new_abbrev_offset == -1U)
+ {
+ dw_cu_ref 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;
+ }
+ }
+ debug_sections[DEBUG_INFO].new_size = total_size;
+ debug_sections[DEBUG_ABBREV].new_size = abbrev_size;
+ debug_sections[DEBUG_TYPES].new_size = types_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
+ in malloced memory, store it as debug_sections[DEBUG_ABBREV].new_data. */
+static void
+write_abbrev (void)
+{
+ dw_cu_ref cu;
+ unsigned char *abbrev = malloc (debug_sections[DEBUG_ABBREV].new_size);
+ unsigned char *ptr = abbrev;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_abbrev\n");
+ }
+
+ if (abbrev == NULL)
+ dwz_oom ();
+
+ debug_sections[DEBUG_ABBREV].new_data = abbrev;
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ {
+ struct abbrev_tag **arr;
+ unsigned int nabbrevs, i, j;
+
+ if (unlikely (fi_multifile)
+ && cu->cu_kind == CU_NORMAL
+ && cu->cu_die->die_remove)
+ continue;
+ if (cu->u1.cu_new_abbrev_owner != NULL)
+ continue;
+ nabbrevs = htab_elements (cu->cu_new_abbrev);
+ htab_traverse (cu->cu_new_abbrev, list_abbrevs, &ob);
+ 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);
+ if (arr[i]->attr[j].form == DW_FORM_implicit_const)
+ write_sleb128 (ptr, arr[i]->values[j]);
+ }
+ *ptr++ = 0;
+ *ptr++ = 0;
+ }
+ *ptr++ = 0;
+ obstack_free (&ob, (void *) arr);
+ if (likely (!low_mem))
+ {
+ htab_delete (cu->cu_new_abbrev);
+ cu->cu_new_abbrev = NULL;
+ }
+ }
+ assert (abbrev + debug_sections[DEBUG_ABBREV].new_size == ptr);
+}
+
+/* Adjust DWARF expression starting at PTR, LEN bytes long, referenced by
+ DIE, with REF being the original DIE. */
+static void
+adjust_exprloc (dw_cu_ref cu, dw_die_ref die, dw_cu_ref refcu,
+ 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 (refcu, refcu->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:
+ case DW_OP_implicit_pointer:
+ case DW_OP_GNU_variable_value:
+ addr = read_size (ptr, refcu->cu_version == 2 ? ptr_size : 4);
+ assert (cu->cu_version == refcu->cu_version);
+ refd = off_htab_lookup (NULL, addr);
+ assert (refd != NULL);
+ refdt = refd;
+ while (refdt->die_toplevel == 0)
+ refdt = refdt->die_parent;
+ if (refdt->die_dup && !refdt->die_op_type_referenced)
+ refd = die_find_dup (refdt, refdt->die_dup, refd);
+ write_size (ptr, cu->cu_version == 2 ? ptr_size : 4,
+ die_cu (refd)->cu_new_offset
+ + refd->u.p2.die_new_offset);
+ if (cu->cu_version == 2)
+ ptr += ptr_size;
+ else
+ ptr += 4;
+ if (op == DW_OP_GNU_implicit_pointer || op == DW_OP_implicit_pointer)
+ skip_leb128 (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:
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_bregx:
+ case DW_OP_bit_piece:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ break;
+ case DW_OP_implicit_value:
+ leni = read_uleb128 (ptr);
+ ptr += leni;
+ break;
+ case DW_OP_GNU_entry_value:
+ case DW_OP_entry_value:
+ leni = read_uleb128 (ptr);
+ assert ((uint64_t) (end - ptr) >= leni);
+ adjust_exprloc (cu, die, refcu, ref, ptr, leni);
+ ptr += leni;
+ break;
+ case DW_OP_GNU_convert:
+ case DW_OP_convert:
+ case DW_OP_GNU_reinterpret:
+ case DW_OP_reinterpret:
+ orig_ptr = ptr;
+ addr = read_uleb128 (ptr);
+ if (addr == 0)
+ break;
+ goto typed_dwarf;
+ case DW_OP_GNU_regval_type:
+ case DW_OP_regval_type:
+ skip_leb128 (ptr);
+ orig_ptr = ptr;
+ addr = read_uleb128 (ptr);
+ goto typed_dwarf;
+ case DW_OP_GNU_const_type:
+ case DW_OP_const_type:
+ orig_ptr = ptr;
+ addr = read_uleb128 (ptr);
+ goto typed_dwarf;
+ case DW_OP_GNU_deref_type:
+ case DW_OP_deref_type:
+ ++ptr;
+ orig_ptr = ptr;
+ addr = read_uleb128 (ptr);
+ typed_dwarf:
+ refd = off_htab_lookup (refcu, refcu->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 || op == DW_OP_const_type)
+ ptr += *ptr + 1;
+ break;
+ default:
+ abort ();
+ }
+ }
+}
+
+/* Write DW_TAG_unit_* DIE (with ORIGIN being the corresponding original DIE) to
+ memory starting at PTR, return pointer after the DIE. */
+static unsigned char *
+write_unit_die (unsigned char *ptr, dw_die_ref die, dw_die_ref origin)
+{
+ struct abbrev_tag *t = die->u.p2.die_new_abbrev;
+ unsigned int i;
+
+ for (i = 0; i < t->nattr; ++i)
+ {
+ struct abbrev_attr *attr = &t->attr[i];
+ switch (attr->attr)
+ {
+ case DW_AT_stmt_list:
+ {
+ enum dwarf_form form;
+ unsigned char *p = get_AT (origin, DW_AT_stmt_list, &form);
+ assert (p && (form == DW_FORM_sec_offset
+ || form == DW_FORM_data4));
+ if (wr_multifile)
+ write_32 (ptr, multi_line_off);
+ else if (op_multifile)
+ write_32 (ptr, 0);
+ else
+ {
+ memcpy (ptr, p, 4);
+ ptr += 4;
+ }
+ }
+ break;
+ case DW_AT_comp_dir:
+ {
+ enum dwarf_form form;
+ unsigned char *p = get_AT (origin, DW_AT_comp_dir, &form);
+ assert (p);
+ assert (form == attr->form
+ || (form == DW_FORM_strp
+ && (attr->form == DW_FORM_GNU_strp_alt
+ || attr->form == DW_FORM_strp_sup)));
+ if (form == DW_FORM_strp)
+ {
+ if (unlikely (wr_multifile || op_multifile || fi_multifile))
+ {
+ unsigned int strp = lookup_strp_offset (read_32 (p));
+ write_32 (ptr, strp);
+ }
+ else
+ {
+ memcpy (ptr, p, 4);
+ ptr += 4;
+ }
+ }
+ else if (form == DW_FORM_line_strp)
+ {
+ memcpy (ptr, p, 4);
+ ptr += 4;
+ }
+ else
+ {
+ size_t len = strlen ((char *) p) + 1;
+ memcpy (ptr, p, len);
+ ptr += len;
+ }
+ }
+ break;
+ case DW_AT_language:
+ {
+ enum dwarf_source_language lang = die_cu (die)->lang;
+ unsigned int lang_size = nr_bytes_for (lang);
+ write_size (ptr, lang_size, lang);
+ ptr += lang_size;
+ }
+ break;
+ default:
+ assert (false);
+ break;
+ }
+ }
+
+ return ptr;
+}
+
+/* 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_cu_ref cu, dw_die_ref die,
+ dw_cu_ref refcu, dw_die_ref ref, unsigned int *die_count)
+{
+ uint64_t low_pc = 0;
+ dw_die_ref child, sib = NULL, origin = NULL;
+ struct abbrev_tag *t;
+
+ if (wr_multifile ? die->die_no_multifile : die->die_remove)
+ return ptr;
+ if (die_count)
+ (*die_count)++;
+ if (die->die_offset == -1U)
+ {
+ if (ref != NULL)
+ ;
+ else if (die_safe_nextdup (die) && die->die_nextdup->die_dup == die)
+ {
+ ref = die->die_nextdup;
+ refcu = die_cu (ref);
+ }
+ if (ref == NULL)
+ origin = die->die_nextdup;
+ }
+ else
+ {
+ ref = die;
+ refcu = cu;
+ if (wr_multifile
+ && (die->die_root || die->die_named_namespace))
+ origin = die;
+ }
+ if (die->die_child && die->die_sib)
+ for (sib = die->die_sib; sib; sib = sib->die_sib)
+ if (wr_multifile ? !sib->die_no_multifile : !sib->die_remove)
+ break;
+ t = die->u.p2.die_new_abbrev;
+ write_uleb128 (ptr, t->entry);
+ if (ref != NULL && origin == NULL)
+ {
+ unsigned char *base
+ = cu->cu_kind == CU_TYPES
+ ? debug_sections[DEBUG_TYPES].data
+ : debug_sections[DEBUG_INFO].data;
+ unsigned char *inptr = base + ref->die_offset;
+ struct abbrev_tag *reft = ref->die_abbrev;
+ unsigned int i, j;
+
+ skip_leb128 (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);
+
+ if (unlikely (wr_multifile || op_multifile)
+ && (reft->attr[i].attr == DW_AT_decl_file
+ || reft->attr[i].attr == DW_AT_call_file))
+ {
+ switch (form)
+ {
+ case DW_FORM_data1:
+ value = read_8 (inptr);
+ break;
+ case DW_FORM_data2:
+ value = read_16 (inptr);
+ break;
+ case DW_FORM_data4:
+ value = read_32 (inptr);
+ break;
+ case DW_FORM_data8:
+ value = read_64 (inptr);
+ break;
+ case DW_FORM_udata:
+ value = read_uleb128 (inptr);
+ break;
+ case DW_FORM_sdata:
+ value = read_sleb128 (inptr);
+ break;
+ case DW_FORM_implicit_const:
+ /* DW_FORM_implicit_const should have been updated
+ already when computing abbrevs. */
+ j++;
+ continue;
+ default: abort ();
+ }
+ value = line_htab_lookup (refcu, value);
+ switch (t->attr[j].form)
+ {
+ case DW_FORM_data1: write_8 (ptr, value); break;
+ case DW_FORM_data2: write_16 (ptr, value); break;
+ case DW_FORM_data4: write_32 (ptr, value); break;
+ case DW_FORM_data8: write_64 (ptr, value); break;
+ case DW_FORM_udata: write_uleb128 (ptr, value); break;
+ case DW_FORM_sdata: write_sleb128 (ptr, value); break;
+ default: abort ();
+ }
+ j++;
+ continue;
+ }
+
+ if (unlikely (fi_multifile)
+ && (reft->attr[i].attr == DW_AT_GNU_macros
+ || reft->attr[i].attr == DW_AT_macros)
+ && alt_macro_htab != NULL)
+ {
+ struct macro_entry me, *m;
+
+ memcpy (ptr, orig_ptr, inptr - orig_ptr);
+ ptr += inptr - orig_ptr;
+ value = read_32 (inptr);
+ me.ptr = debug_sections[DEBUG_MACRO].data + value;
+ m = (struct macro_entry *)
+ htab_find_with_hash (macro_htab, &me, value);
+ write_32 (ptr, m->hash);
+ j++;
+ continue;
+ }
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ {
+ dw_die_ref refd, refdt;
+ if (t->attr[j].form != DW_FORM_GNU_ref_alt
+ && t->attr[j].form != DW_FORM_ref_sup4)
+ {
+ memcpy (ptr, orig_ptr, inptr - orig_ptr);
+ ptr += inptr - orig_ptr;
+ }
+ value = read_size (inptr, refcu->cu_version == 2
+ ? ptr_size : 4);
+ inptr += refcu->cu_version == 2 ? ptr_size : 4;
+ refd = off_htab_lookup (NULL, value);
+ if (refd == NULL || refd->die_tag == 0)
+ error (1, 0, "Couldn't find DIE at DW_FORM_ref_addr offset"
+ " 0x%" PRIx64, value);
+ assert (refd != NULL);
+ refdt = refd;
+ while (refdt->die_toplevel == 0)
+ refdt = refdt->die_parent;
+ if (refdt->die_dup && !refdt->die_op_type_referenced)
+ {
+ refd = die_find_dup (refdt, refdt->die_dup, refd);
+ if (t->attr[j].form == DW_FORM_GNU_ref_alt
+ || t->attr[j].form == DW_FORM_ref_sup4)
+ {
+ assert (die_cu (refd)->cu_kind == CU_ALT);
+ write_32 (ptr, refd->die_offset);
+ j++;
+ continue;
+ }
+ }
+ assert (refd->u.p2.die_new_offset
+ && t->attr[j].form != DW_FORM_GNU_ref_alt
+ && t->attr[j].form != DW_FORM_ref_sup4);
+ value = die_cu (refd)->cu_new_offset
+ + refd->u.p2.die_new_offset;
+ write_size (ptr, cu->cu_version == 2 ? ptr_size : 4,
+ value);
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ if (unlikely (op_multifile))
+ assert (die_cu (refd)->cu_kind == CU_PU);
+ j++;
+ continue;
+ }
+ case DW_FORM_addr:
+ inptr += ptr_size;
+ if (reft->attr[i].attr == DW_AT_low_pc)
+ low_pc = read_size (inptr - ptr_size, ptr_size);
+ if (reft->attr[i].attr == DW_AT_high_pc
+ && t->attr[j].form != reft->attr[i].form)
+ {
+ uint64_t high_pc = read_size (inptr - ptr_size, ptr_size);
+ switch (t->attr[j].form)
+ {
+ case DW_FORM_udata:
+ write_uleb128 (ptr, high_pc - low_pc);
+ break;
+ case DW_FORM_data4:
+ write_32 (ptr, high_pc - low_pc);
+ break;
+ default:
+ abort ();
+ }
+ j++;
+ continue;
+ }
+ break;
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ break;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ ++inptr;
+ break;
+ case DW_FORM_data2:
+ inptr += 2;
+ break;
+ case DW_FORM_data4:
+ if (reft->attr[i].attr == DW_AT_high_pc
+ && t->attr[j].form != reft->attr[i].form)
+ {
+ uint32_t range_len = read_32 (inptr);
+ switch (t->attr[j].form)
+ {
+ case DW_FORM_udata:
+ write_uleb128 (ptr, range_len);
+ break;
+ default:
+ abort ();
+ }
+ j++;
+ continue;
+ }
+ inptr += 4;
+ break;
+ case DW_FORM_sec_offset:
+ inptr += 4;
+ break;
+ case DW_FORM_data8:
+ if (reft->attr[i].attr == DW_AT_high_pc
+ && t->attr[j].form != reft->attr[i].form)
+ {
+ uint64_t range_len = read_64 (inptr);
+ switch (t->attr[j].form)
+ {
+ case DW_FORM_udata:
+ write_uleb128 (ptr, range_len);
+ break;
+ case DW_FORM_data4:
+ write_32 (ptr, range_len);
+ break;
+ default:
+ abort ();
+ }
+ j++;
+ continue;
+ }
+ inptr += 8;
+ break;
+ case DW_FORM_ref_sig8:
+ inptr += 8;
+ break;
+ case DW_FORM_data16:
+ inptr += 16;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ skip_leb128 (inptr);
+ break;
+ case DW_FORM_strp:
+ if (unlikely (wr_multifile || op_multifile || fi_multifile))
+ {
+ unsigned int strp = lookup_strp_offset (read_32 (inptr));
+ memcpy (ptr, orig_ptr, inptr - 4 - orig_ptr);
+ ptr += inptr - 4 - orig_ptr;
+ write_32 (ptr, strp);
+ j++;
+ continue;
+ }
+ inptr += 4;
+ break;
+ case DW_FORM_line_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 == t->nattr
+ || t->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 (refcu, refcu->cu_offset + value);
+ assert (refd != NULL);
+ refdt = refd;
+ while (refdt->die_toplevel == 0)
+ refdt = refdt->die_parent;
+ if (refdt->die_dup && refdt->die_op_type_referenced
+ && cu->cu_kind != CU_PU)
+ {
+ if (cu == die_cu (refdt->die_dup))
+ refd = die_find_dup (refdt, refdt->die_dup, refd);
+ }
+ else if (refdt->die_dup)
+ refd = die_find_dup (refdt, refdt->die_dup, refd);
+ if (t->attr[j].form == DW_FORM_GNU_ref_alt
+ || t->attr[j].form == DW_FORM_ref_sup4)
+ {
+ value = refd->die_offset;
+ assert (die_cu (refd)->cu_kind == CU_ALT);
+ }
+ else
+ {
+ dw_cu_ref refdcu = die_cu (refd);
+ value = refd->u.p2.die_new_offset;
+ assert (IMPLIES (cu->cu_kind == CU_PU,
+ die_cu (refd)->cu_kind == CU_PU));
+ assert (value && refdcu->cu_kind != CU_ALT);
+ if (t->attr[j].form == DW_FORM_ref_addr)
+ {
+ value += refdcu->cu_new_offset;
+ if (unlikely (op_multifile))
+ assert (refdcu->cu_kind == CU_PU);
+ }
+ else
+ assert (refdcu == cu);
+ }
+ }
+ switch (t->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, cu->cu_version == 2 ? ptr_size : 4,
+ value);
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ break;
+ case DW_FORM_GNU_ref_alt:
+ case DW_FORM_ref_sup4:
+ write_32 (ptr, value);
+ break;
+ case DW_FORM_ref_sup8:
+ write_64 (ptr, value);
+ 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;
+
+ /* Old DWARF uses blocks instead of exprlocs. */
+ if (form == DW_FORM_block1 && cu->cu_version < 4)
+ 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_rank:
+ case DW_AT_call_value:
+ case DW_AT_call_target:
+ case DW_AT_call_target_clobbered:
+ case DW_AT_call_data_location:
+ case DW_AT_call_data_value:
+ 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 (cu, die, refcu, ref, ptr - len, len);
+ default:
+ break;
+ }
+ else if (form == DW_FORM_exprloc)
+ adjust_exprloc (cu, die, refcu, ref, ptr - len, len);
+ j++;
+ }
+ assert (j == t->nattr);
+ }
+ else
+ switch (die->die_tag)
+ {
+ case DW_TAG_partial_unit:
+ case DW_TAG_compile_unit:
+ ptr = write_unit_die (ptr, die, origin);
+ break;
+ case DW_TAG_namespace:
+ case DW_TAG_module:
+ {
+ enum dwarf_form form;
+ unsigned char *p = get_AT (origin, DW_AT_name, &form);
+ assert (p && (form == t->attr[0].form
+ || (form == DW_FORM_strp
+ && (t->attr[0].form == DW_FORM_GNU_strp_alt
+ || t->attr[0].form == DW_FORM_strp_sup))));
+ if (form == DW_FORM_strp)
+ {
+ if (unlikely (wr_multifile || op_multifile || fi_multifile))
+ {
+ unsigned int strp = lookup_strp_offset (read_32 (p));
+ write_32 (ptr, strp);
+ }
+ else
+ {
+ memcpy (ptr, p, 4);
+ ptr += 4;
+ }
+ }
+ else if (form == DW_FORM_line_strp)
+ {
+ memcpy (ptr, p, 4);
+ ptr += 4;
+ }
+ else
+ {
+ size_t len = strlen ((char *) p) + 1;
+ memcpy (ptr, p, len);
+ ptr += len;
+ }
+ if (t->nattr > 1)
+ {
+ assert (sib);
+ switch (t->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:
+ refcu = die_cu (origin);
+ if (t->attr[0].form == DW_FORM_GNU_ref_alt
+ || t->attr[0].form == DW_FORM_ref_sup4)
+ {
+ assert (refcu->cu_kind == CU_ALT);
+ write_32 (ptr, origin->die_offset);
+ break;
+ }
+ assert (refcu->cu_kind != CU_ALT);
+ write_size (ptr, cu->cu_version == 2 ? ptr_size : 4,
+ refcu->cu_new_offset
+ + origin->u.p2.die_new_offset);
+ ptr += 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, cu, child, refcu, ref_child, die_count);
+ }
+ else
+ for (child = die->die_child; child; child = child->die_sib)
+ ptr = write_die (ptr, cu, child, NULL, NULL, die_count);
+ if (die->die_child)
+ write_8 (ptr, 0);
+ return ptr;
+}
+
+/* Recompute abbrevs for CU. If any children were collapsed during
+ compute_abbrevs, their ->u.p2.die_new_abbrev and ->u.p2.die_new_offset
+ fields are no longer available and need to be computed again. */
+static void
+recompute_abbrevs (dw_cu_ref cu, unsigned int cu_size)
+{
+ unsigned int headersz = (cu->cu_kind == CU_TYPES
+ ? 23 : (cu->cu_version >= 5 ? 12 : 11));
+ struct abbrev_tag *t;
+ unsigned int ndies = 0, intracusize, off, i;
+ dw_die_ref *intracuarr, *intracuvec;
+
+ t = (struct abbrev_tag *)
+ obstack_alloc (&ob2,
+ sizeof (*t)
+ + (max_nattr + 4) * sizeof (struct abbrev_attr)
+ + (max_nattr + 4) * sizeof (int64_t));
+ t->values = (int64_t *) &t->attr[max_nattr + 4];
+
+ build_abbrevs_for_die (cu->u1.cu_new_abbrev_owner
+ ? cu->u1.cu_new_abbrev_owner->cu_new_abbrev
+ : cu->cu_new_abbrev, cu, cu->cu_die, NULL, NULL, t,
+ &ndies, &ob2, true);
+
+ obstack_ptr_grow (&ob2, NULL);
+ intracuarr = (dw_die_ref *) obstack_finish (&ob2);
+ if (cu->cu_intracu_form != DW_FORM_ref_udata)
+ {
+ switch (cu->cu_intracu_form)
+ {
+ case DW_FORM_ref1: intracusize = 1; break;
+ case DW_FORM_ref2: intracusize = 2; break;
+ case DW_FORM_ref4: intracusize = 4; break;
+ default: abort ();
+ }
+ off = init_new_die_offsets (cu->cu_die, headersz, intracusize);
+ }
+ else
+ {
+ intracusize = cu->initial_intracusize;
+
+ off = init_new_die_offsets (cu->cu_die, headersz, intracusize);
+ do
+ {
+ intracuvec = intracuarr;
+ i = update_new_die_offsets (cu->cu_die, headersz, &intracuvec);
+ assert (*intracuvec == NULL);
+ if (i == off)
+ break;
+ assert (i < off);
+ off = i;
+ }
+ while (1);
+
+ intracuvec = intracuarr;
+ off = finalize_new_die_offsets (cu, cu->cu_die, headersz, 0,
+ &intracuvec);
+ assert (*intracuvec == NULL);
+ }
+ obstack_free (&ob2, (void *) t);
+ assert (off == cu_size);
+}
+
+/* Construct new .debug_info section in malloced memory,
+ store it to debug_sections[DEBUG_INFO].new_data. */
+static void
+write_info (unsigned int *die_count)
+{
+ dw_cu_ref cu, cu_next;
+ unsigned char *info = malloc (debug_sections[DEBUG_INFO].new_size);
+ unsigned char *ptr = info;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_info\n");
+ }
+
+ if (info == NULL)
+ dwz_oom ();
+ if (die_count)
+ *die_count = 0;
+ debug_sections[DEBUG_INFO].new_data = info;
+ cu = first_cu;
+ if (unlikely (fi_multifile))
+ while (cu
+ && cu->cu_kind == CU_NORMAL
+ && cu->cu_die->die_remove)
+ cu = cu->cu_next;
+ for (; cu; cu = cu_next)
+ {
+ unsigned long next_off = debug_sections[DEBUG_INFO].new_size;
+ /* Ignore .debug_types CUs. */
+ if (cu->cu_kind == CU_TYPES)
+ break;
+ cu_next = cu->cu_next;
+ if (unlikely (fi_multifile))
+ while (cu_next
+ && cu_next->cu_kind == CU_NORMAL
+ && cu_next->cu_die->die_remove)
+ cu_next = cu_next->cu_next;
+ if (cu_next && cu_next->cu_kind == CU_TYPES)
+ cu_next = NULL;
+ if (cu_next)
+ next_off = cu_next->cu_new_offset;
+ else if (wr_multifile)
+ next_off += multi_info_off - 11L;
+ if (unlikely (low_mem)
+ && cu->cu_kind != CU_PU
+ && expand_children (cu->cu_die))
+ recompute_abbrevs (cu, next_off - cu->cu_new_offset);
+ /* Write CU header. */
+ write_32 (ptr, next_off - cu->cu_new_offset - 4);
+ write_16 (ptr, cu->cu_version);
+ if (cu->cu_version >= 5)
+ {
+ *ptr++ = (cu->cu_die->die_tag == DW_TAG_compile_unit
+ ? DW_UT_compile : DW_UT_partial);
+ write_8 (ptr, ptr_size);
+ }
+ write_32 (ptr, cu->u2.cu_new_abbrev_offset);
+ if (cu->cu_version < 5)
+ write_8 (ptr, ptr_size);
+ ptr = write_die (ptr, cu, cu->cu_die, NULL, NULL, die_count);
+ assert (info + (next_off - (wr_multifile ? multi_info_off : 0)) == ptr);
+ if (unlikely (low_mem) && cu->cu_kind != CU_PU)
+ collapse_children (cu, cu->cu_die);
+ }
+ if (wr_multifile)
+ {
+ /* And terminate the contribution by the current object file. */
+ write_32 (ptr, 7);
+ write_16 (ptr, 2);
+ write_32 (ptr, 0);
+ write_8 (ptr, ptr_size);
+ }
+ assert (info + debug_sections[DEBUG_INFO].new_size == ptr);
+}
+
+/* Adjust .debug_loc range determined by *SLOT, called through
+ htab_traverse. */
+static int
+adjust_loclist (void **slot, void *data)
+{
+ struct debug_loc_adjust *adj = (struct debug_loc_adjust *) *slot;
+ unsigned char *ptr, *endsec;
+ GElf_Addr low, high;
+ size_t len;
+
+ (void)data;
+
+ ptr = debug_sections[DEBUG_LOC].new_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, adj->cu->cu_die, adj->cu, adj->cu->cu_die,
+ ptr, len);
+
+ ptr += len;
+ }
+
+ return 1;
+}
+
+/* Adjust .debug_loclists range determined by *SLOT, called through
+ htab_traverse. */
+static int
+adjust_loclists (void **slot, void *data)
+{
+ struct debug_loc_adjust *adj = (struct debug_loc_adjust *) *slot;
+ unsigned char *ptr, *endsec;
+ size_t len = 0;
+
+ (void)data;
+
+ ptr = debug_sections[DEBUG_LOCLISTS].new_data + adj->start_offset;
+ endsec = ptr + debug_sections[DEBUG_LOCLISTS].size;
+
+again:
+ while (ptr < endsec)
+ {
+ uint8_t lle = *ptr++;
+ switch (lle)
+ {
+ case DW_LLE_end_of_list:
+ goto done;
+
+ case DW_LLE_base_addressx:
+ skip_leb128 (ptr);
+ goto again;
+
+ case DW_LLE_startx_endx:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_startx_length:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_offset_pair:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_default_location:
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_base_address:
+ ptr += ptr_size;
+ goto again;
+
+ case DW_LLE_start_end:
+ ptr += 2 * ptr_size;
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_start_length:
+ ptr += ptr_size;
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_GNU_view_pair:
+ /* Note we cannot check CU version here, but we'll get a
+ warning on the original parsing if CU version is not 5.*/
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ goto again;
+
+ default:
+ error (0, 0, "unhandled location list entry 0x%x", lle);
+ return 1;
+ }
+
+ assert (ptr + len <= endsec);
+
+ adjust_exprloc (adj->cu, adj->cu->cu_die, adj->cu, adj->cu->cu_die,
+ ptr, len);
+
+ ptr += len;
+ }
+
+done:
+ return 1;
+}
+
+/* Create new .debug_loc section in malloced memory if .debug_loc
+ needs to be adjusted. */
+static void
+write_loc (void)
+{
+ unsigned char *loc;
+ if (loc_htab == NULL)
+ return;
+ loc = malloc (debug_sections[DEBUG_LOC].size);
+ if (loc == NULL)
+ dwz_oom ();
+ memcpy (loc, debug_sections[DEBUG_LOC].data, debug_sections[DEBUG_LOC].size);
+ debug_sections[DEBUG_LOC].new_data = loc;
+ htab_traverse (loc_htab, adjust_loclist, NULL);
+}
+
+/* Create new .debug_loclists section in malloced memory if .debug_loclists
+ needs to be adjusted. */
+static void
+write_loclists (void)
+{
+ unsigned char *loc;
+ if (loclists_htab == NULL)
+ return;
+ loc = malloc (debug_sections[DEBUG_LOCLISTS].size);
+ if (loc == NULL)
+ dwz_oom ();
+ memcpy (loc, debug_sections[DEBUG_LOCLISTS].data,
+ debug_sections[DEBUG_LOCLISTS].size);
+ debug_sections[DEBUG_LOCLISTS].new_data = loc;
+ htab_traverse (loclists_htab, adjust_loclists, NULL);
+}
+
+/* Create new .debug_types section in malloced memory. */
+static void
+write_types (void)
+{
+ dw_cu_ref cu;
+ unsigned char *types, *ptr, *inptr;
+ dw_die_ref ref;
+
+ if (debug_sections[DEBUG_TYPES].data == NULL)
+ return;
+ types = malloc (debug_sections[DEBUG_TYPES].new_size);
+ if (types == NULL)
+ dwz_oom ();
+ debug_sections[DEBUG_TYPES].new_data = types;
+ ptr = types;
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ {
+ unsigned long next_off = debug_sections[DEBUG_TYPES].new_size;
+ /* Ignore .debug_info CUs. */
+ if (cu->cu_kind != CU_TYPES)
+ continue;
+ if (cu->cu_next)
+ next_off = cu->cu_next->cu_new_offset;
+ if (unlikely (low_mem)
+ && expand_children (cu->cu_die))
+ recompute_abbrevs (cu, next_off - cu->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);
+ inptr = debug_sections[DEBUG_TYPES].data + cu->cu_offset + 19;
+ memcpy (ptr, inptr - 8, 8);
+ ptr += 8;
+ ref = off_htab_lookup (cu, cu->cu_offset + read_32 (inptr));
+ assert (ref && die_safe_dup(ref) == NULL);
+ write_32 (ptr, ref->u.p2.die_new_offset);
+ ptr = write_die (ptr, cu, cu->cu_die, NULL, NULL, NULL);
+ assert (types + next_off == ptr);
+ if (unlikely (low_mem))
+ collapse_children (cu, cu->cu_die);
+ }
+ assert (types + debug_sections[DEBUG_TYPES].new_size == ptr);
+}
+
+/* Construct new .debug_aranges section in malloced memory,
+ store it to debug_sections[DEBUG_ARANGES].new_data. */
+static int
+write_aranges (DSO *dso)
+{
+ dw_cu_ref cu, cufirst = NULL, cucur;
+ unsigned char *aranges, *ptr, *end;
+
+ if (debug_sections[DEBUG_ARANGES].data == NULL)
+ return 0;
+
+ aranges = malloc (debug_sections[DEBUG_ARANGES].size);
+ if (aranges == NULL)
+ dwz_oom ();
+ memcpy (aranges, debug_sections[DEBUG_ARANGES].data,
+ debug_sections[DEBUG_ARANGES].size);
+ debug_sections[DEBUG_ARANGES].new_data = aranges;
+ ptr = aranges;
+ end = aranges + debug_sections[DEBUG_ARANGES].size;
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (cu->cu_kind != CU_PU)
+ break;
+ cufirst = cu;
+ while (ptr < end)
+ {
+ unsigned int culen, value, cuoff;
+
+ if (end - ptr < 12)
+ {
+ error (0, 0, "%s: Corrupted .debug_aranges section",
+ dso->filename);
+ return 1;
+ }
+ culen = read_32 (ptr);
+ if (culen >= 0xfffffff0)
+ {
+ error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+ return 1;
+ }
+
+ value = read_16 (ptr);
+ if (value != 2)
+ {
+ error (0, 0, "%s: DWARF version %d in .debug_aranges unhandled",
+ dso->filename, value);
+ return 1;
+ }
+
+ cuoff = read_32 (ptr);
+ cucur = cu;
+ /* Optimistically assume that .debug_aranges CU offsets only increase,
+ otherwise this might be too expensive and need a hash table. */
+ for (; cu; cu = cu->cu_next)
+ {
+ if (cu->cu_kind == CU_TYPES)
+ {
+ cu = NULL;
+ break;
+ }
+ else if (cu->cu_offset == cuoff)
+ break;
+ }
+ if (cu == NULL)
+ {
+ for (cu = cufirst; cu != cucur; cu = cu->cu_next)
+ if (cu->cu_offset == cuoff)
+ break;
+ if (cu == cucur)
+ {
+ error (0, 0, "%s: Couldn't find CU for .debug_aranges "
+ "offset 0x%x", dso->filename, cuoff);
+ return 1;
+ }
+ }
+ if (unlikely (fi_multifile)
+ && cu->cu_kind == CU_NORMAL
+ && cu->cu_die->die_remove)
+ {
+ error (0, 0, "%s: Partial unit referenced in .debug_aranges",
+ dso->filename);
+ return 1;
+ }
+ ptr -= 4;
+ write_32 (ptr, cu->cu_new_offset);
+ ptr += culen - 6;
+ }
+ return 0;
+}
+
+/* Helper function of write_gdb_index, called through qsort.
+ Sort an array of unsigned integer pairs, by increasing
+ first integer. The first integer is the TU offset
+ in the .gdb_index TU table, the second is its index in
+ the TU table from the start of that table. */
+static int
+gdb_index_tu_cmp (const void *p, const void *q)
+{
+ unsigned int *t1 = (unsigned int *) p;
+ unsigned int *t2 = (unsigned int *) q;
+
+ if (t1[0] < t2[0])
+ return -1;
+ if (t1[0] > t2[0])
+ return 1;
+
+ if (t1[1] < t2[1])
+ return -1;
+ if (t1[1] > t2[1])
+ return 1;
+ return 0;
+}
+
+/* Construct new .gdb_index section in malloced memory
+ if it needs adjustment. */
+static void
+write_gdb_index (void)
+{
+ dw_cu_ref cu, cu_next, first_tu = NULL;
+ unsigned char *gdb_index, *ptr, *inptr, *end;
+ unsigned int ncus = 0, npus = 0, ntus = 0, ndelcus = 0, ver;
+ unsigned int culistoff, cutypesoff, addressoff, symboloff, constoff;
+ unsigned int *tuindices = NULL, tuidx = 0, *cumap = NULL, i, j, k;
+ bool fail = false;
+
+ debug_sections[GDB_INDEX].new_size = 0;
+ if (likely (!op_multifile)
+ && (debug_sections[GDB_INDEX].data == NULL
+ || debug_sections[GDB_INDEX].size < 0x18))
+ return;
+ inptr = (unsigned char *) debug_sections[GDB_INDEX].data;
+ if (unlikely (op_multifile))
+ ver = multi_gdb_index_ver;
+ else
+ ver = buf_read_ule32 (inptr);
+ if (ver < 4 || ver > 8)
+ return;
+
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (cu->cu_kind == CU_PU)
+ npus++;
+ else if (cu->cu_kind == CU_NORMAL)
+ {
+ ncus++;
+ if (unlikely (fi_multifile) && cu->cu_die->die_remove)
+ ndelcus++;
+ }
+ else if (cu->cu_kind == CU_TYPES)
+ ntus++;
+
+ /* Starting with version 7 CU indexes are limited to 24 bits,
+ so if we have more CUs, give up. */
+ if (npus + ncus + ntus - ndelcus >= (1U << 24))
+ return;
+
+ if (unlikely (op_multifile))
+ {
+ assert (ncus == 0 && ntus == 0);
+ debug_sections[GDB_INDEX].new_size
+ = 0x18 + npus * 16 + 16;
+ gdb_index = malloc (debug_sections[GDB_INDEX].new_size);
+ if (gdb_index == NULL)
+ dwz_oom ();
+ debug_sections[GDB_INDEX].new_data = gdb_index;
+ /* Write new header. */
+ buf_write_le32 (gdb_index + 0x00, ver);
+ buf_write_le32 (gdb_index + 0x04, 0x18);
+ buf_write_le32 (gdb_index + 0x08, 0x18 + npus * 16);
+ buf_write_le32 (gdb_index + 0x0c, 0x18 + npus * 16);
+ buf_write_le32 (gdb_index + 0x10, 0x18 + npus * 16);
+ buf_write_le32 (gdb_index + 0x14, 0x18 + npus * 16 + 16);
+ ptr = gdb_index + 0x18;
+ /* Write new CU list. */
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ {
+ unsigned long next_off = debug_sections[DEBUG_INFO].new_size;
+ if (cu->cu_next)
+ next_off = cu->cu_next->cu_new_offset;
+ buf_write_le64 (ptr, cu->cu_new_offset);
+ buf_write_le64 (ptr + 8, next_off - cu->cu_new_offset);
+ ptr += 16;
+ }
+ /* Write an empty hash table (with two entries). */
+ memset (ptr, '\0', 16);
+ return;
+ }
+
+ culistoff = buf_read_ule32 (inptr + 0x04);
+ cutypesoff = buf_read_ule32 (inptr + 0x08);
+ addressoff = buf_read_ule32 (inptr + 0x0c);
+ symboloff = buf_read_ule32 (inptr + 0x10);
+ constoff = buf_read_ule32 (inptr + 0x14);
+ if (culistoff != 0x18
+ || cutypesoff != 0x18 + ncus * 16
+ || addressoff != cutypesoff + ntus * 24
+ || symboloff < addressoff
+ || ((symboloff - addressoff) % 20) != 0
+ || constoff < symboloff
+ || ((constoff - symboloff) & (constoff - symboloff - 1)) != 0
+ || ((constoff - symboloff) & 7) != 0
+ || debug_sections[GDB_INDEX].size < constoff)
+ return;
+ inptr += 0x18;
+ if (ndelcus)
+ cumap = (unsigned int *)
+ obstack_alloc (&ob2, ncus * sizeof (unsigned int));
+ for (cu = first_cu, i = 0, j = 0; cu; cu = cu->cu_next)
+ if (cu->cu_kind == CU_NORMAL)
+ {
+ if (buf_read_ule64 (inptr) != cu->cu_offset)
+ {
+ if (cumap)
+ obstack_free (&ob2, (void *) cumap);
+ return;
+ }
+ inptr += 16;
+ if (cumap)
+ {
+ if (cu->cu_die->die_remove)
+ cumap[i++] = -1U;
+ else
+ cumap[i++] = j++;
+ }
+ }
+ else if (cu->cu_kind == CU_TYPES)
+ {
+ if (tuindices == NULL)
+ {
+ tuindices = (unsigned int *)
+ obstack_alloc (&ob2, ntus * 2 * sizeof (unsigned int));
+ first_tu = cu;
+ }
+ tuindices[2 * tuidx] = buf_read_ule64 (inptr);
+ tuindices[2 * tuidx + 1] = tuidx * 24;
+ tuidx++;
+ inptr += 24;
+ }
+ if (ntus)
+ {
+ qsort (tuindices, ntus, 2 * sizeof (unsigned int), gdb_index_tu_cmp);
+ for (tuidx = 0, cu = first_tu; tuidx < ntus; tuidx++, cu = cu->cu_next)
+ if (tuindices[2 * tuidx] != cu->cu_offset)
+ {
+ if (cumap)
+ obstack_free (&ob2, (void *) cumap);
+ else
+ obstack_free (&ob2, (void *) tuindices);
+ return;
+ }
+ }
+
+ if (multifile
+ && !fi_multifile
+ && !low_mem
+ && multi_gdb_index_ver < ver)
+ multi_gdb_index_ver = ver;
+
+ debug_sections[GDB_INDEX].new_size
+ = debug_sections[GDB_INDEX].size + npus * 16 - ndelcus * 16;
+ gdb_index = malloc (debug_sections[GDB_INDEX].new_size);
+ if (gdb_index == NULL)
+ dwz_oom ();
+ debug_sections[GDB_INDEX].new_data = gdb_index;
+ /* Write new header. */
+ buf_write_le32 (gdb_index + 0x00, ver);
+ buf_write_le32 (gdb_index + 0x04, culistoff);
+ buf_write_le32 (gdb_index + 0x08, cutypesoff + npus * 16 - ndelcus * 16);
+ buf_write_le32 (gdb_index + 0x0c, addressoff + npus * 16 - ndelcus * 16);
+ buf_write_le32 (gdb_index + 0x10, symboloff + npus * 16 - ndelcus * 16);
+ buf_write_le32 (gdb_index + 0x14, constoff + npus * 16 - ndelcus * 16);
+ ptr = gdb_index + 0x18;
+ /* Write new CU list. */
+ for (cu = first_cu; cu; cu = cu_next)
+ {
+ unsigned long next_off = debug_sections[DEBUG_INFO].new_size;
+ if (cu->cu_kind == CU_TYPES)
+ break;
+ cu_next = cu->cu_next;
+ if (unlikely (fi_multifile))
+ {
+ while (cu_next
+ && cu_next->cu_kind == CU_NORMAL
+ && cu_next->cu_die->die_remove)
+ cu_next = cu_next->cu_next;
+ if (cu->cu_die->die_remove)
+ continue;
+ }
+ if (cu_next && cu_next->cu_kind != CU_TYPES)
+ next_off = cu_next->cu_new_offset;
+ buf_write_le64 (ptr, cu->cu_new_offset);
+ buf_write_le64 (ptr + 8, next_off - cu->cu_new_offset);
+ ptr += 16;
+ }
+ /* Write new TU list. */
+ for (tuidx = 0; cu; cu = cu->cu_next, tuidx++)
+ {
+ unsigned char *p;
+ unsigned int tuoff = tuindices[2 * tuidx + 1];
+ dw_die_ref ref;
+ assert (cu->cu_kind == CU_TYPES);
+ buf_write_le64 (ptr + tuoff, cu->cu_new_offset);
+ p = debug_sections[DEBUG_TYPES].data + cu->cu_offset + 19;
+ ref = off_htab_lookup (cu, cu->cu_offset + read_32 (p));
+ assert (ref && ref->die_dup == NULL);
+ buf_write_le64 (ptr + tuoff + 8, ref->u.p2.die_new_offset);
+ p -= 12;
+ buf_write_le64 (ptr + tuoff + 16, read_64 (p));
+ }
+ ptr += ntus * 24;
+ end = inptr + (symboloff - addressoff);
+ /* Copy address area, adjusting all CU indexes. */
+ while (inptr < end)
+ {
+ memcpy (ptr, inptr, 16);
+ i = buf_read_ule32 (inptr + 16);
+ if (cumap && i < ncus)
+ {
+ if (cumap[i] == -1U)
+ fail = true;
+ i = cumap[i] + npus;
+ }
+ else
+ i += npus - ndelcus;
+ buf_write_le32 (ptr + 16, i);
+ ptr += 20;
+ inptr += 20;
+ }
+ /* Copy the symbol hash table. */
+ memcpy (ptr, inptr, constoff - symboloff);
+ /* Clear the const pool initially. */
+ memset (ptr + (constoff - symboloff), '\0',
+ debug_sections[GDB_INDEX].size - constoff);
+ ptr = ptr + (constoff - symboloff);
+ end = inptr + (constoff - symboloff);
+ /* Finally copy over const objects into the const pool, strings as is,
+ CU vectors with CU indexes adjusted. */
+ while (inptr < end)
+ {
+ unsigned int name = buf_read_ule32 (inptr);
+ unsigned int cuvec = buf_read_ule32 (inptr + 4);
+
+ inptr += 8;
+ if (name == 0 && cuvec == 0)
+ continue;
+ if (name > debug_sections[GDB_INDEX].size - constoff - 1
+ || cuvec > debug_sections[GDB_INDEX].size - constoff - 4)
+ {
+ fail:
+ free (gdb_index);
+ debug_sections[GDB_INDEX].new_size = 0;
+ return;
+ }
+ if (ptr[name] == '\0')
+ {
+ unsigned char *strend = end + name;
+ while (*strend != '\0')
+ {
+ if (strend + 1
+ == end + (debug_sections[GDB_INDEX].size - constoff))
+ goto fail;
+ strend++;
+ }
+ memcpy (ptr + name, end + name, strend + 1 - (end + name));
+ }
+ if (buf_read_ule32 (ptr + cuvec) == 0)
+ {
+ unsigned int count = buf_read_ule32 (end + cuvec);
+ if (count * 4
+ > debug_sections[GDB_INDEX].size - constoff - cuvec - 4)
+ goto fail;
+ buf_write_le32 (ptr + cuvec, count);
+ for (i = 0; i < count; i++)
+ {
+ j = buf_read_ule32 (end + cuvec + (i + 1) * 4);
+ if (ver >= 7)
+ k = j & ((1U << 24) - 1);
+ else
+ k = j;
+ if (cumap && k < ncus)
+ {
+ if (cumap[k] == -1U)
+ fail = true;
+ k = cumap[k] + npus;
+ }
+ else
+ k += npus - ndelcus;
+ if (ver >= 7)
+ j = (j & (~0U << 24)) | k;
+ else
+ j = k;
+ buf_write_le32 (ptr + cuvec + (i + 1) * 4, j);
+ }
+ }
+ }
+ if (cumap)
+ obstack_free (&ob2, (void *) cumap);
+ else if (tuindices)
+ obstack_free (&ob2, (void *) tuindices);
+ if (fail)
+ {
+ free (debug_sections[GDB_INDEX].new_data);
+ debug_sections[GDB_INDEX].new_data = NULL;
+ debug_sections[GDB_INDEX].new_size = 0;
+ }
+}
+
+/* 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;
+}
+
+/* Initialize do_read_* and do_write_* callbacks based on
+ ENDIANITY. */
+static void
+init_endian (int endianity)
+{
+ if (endianity == 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 (endianity == 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
+ abort ();
+}
+
+/* Read DWARF sections from DSO. */
+static int
+read_dwarf (DSO *dso, bool quieter, unsigned int *die_count)
+{
+ Elf_Data *data;
+ Elf_Scn *scn;
+ int i, j;
+
+ 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
+ || strcmp (name, ".gdb_index") == 0
+ || strcmp (name, ".gnu_debugaltlink") == 0)
+ {
+ if (dso->shdr[i].sh_flags & SHF_COMPRESSED)
+ {
+ error (0, 0,
+ "%s: Found compressed %s section, not attempting dwz"
+ " compression",
+ dso->filename, name);
+ return 1;
+ }
+ 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);
+ if (data->d_buf == NULL)
+ {
+ error (0, 0, "%s: Found empty %s section, not attempting"
+ " dwz compression", dso->filename, name);
+ return 1;
+ }
+ 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].new_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
+ || dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+ init_endian (dso->ehdr.e_ident[EI_DATA]);
+ else
+ {
+ error (0, 0, "%s: Wrong ELF data encoding", dso->filename);
+ return 1;
+ }
+
+ if (debug_sections[DEBUG_INFO].data == NULL
+ && !rd_multifile)
+ {
+ if (!quieter)
+ error (0, 0, "%s: .debug_info section not present",
+ dso->filename);
+ return 3;
+ }
+
+ if (debug_sections[DEBUG_SUP].data != NULL && !(dwarf_5 && rd_multifile))
+ {
+ error (0, 0, "%s: .debug_sup section already present",
+ dso->filename);
+ return 1;
+ }
+
+ if (debug_sections[GNU_DEBUGALTLINK].data != NULL)
+ {
+ error (0, 0, "%s: .gnu_debugaltlink section already present",
+ dso->filename);
+ return 1;
+ }
+
+ int res;
+ res = read_debug_info (dso, DEBUG_INFO, die_count);
+ if (res == 0 && stats_p)
+ print_parse_stats ();
+ return res;
+}
+
+/* Open an ELF file NAME. */
+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
+ && (!rd_multifile || ehdr.e_type != ET_REL))
+ {
+ error (0, 0, "\"%s\" is not a shared library", name);
+ goto error_out;
+ }
+
+ /* Allocate DSO structure. */
+ dso = (DSO *)
+ malloc (sizeof(DSO) + ehdr.e_shnum * sizeof(GElf_Shdr)
+ + ehdr.e_shnum * sizeof(Elf_Scn *)
+ + strlen (name) + 1);
+ 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];
+
+ 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 *) &dso->scn[ehdr.e_shnum];
+ strcpy ((char *) &dso->scn[ehdr.e_shnum], name);
+ return dso;
+
+error_out:
+ free (dso);
+ if (elf)
+ elf_end (elf);
+ if (fd != -1)
+ close (fd);
+ return NULL;
+}
+
+/* Implicit arg for compare_section_numbers. Could be passed in as explicit arg
+ when using qsort_r instead. */
+static DSO *compare_section_numbers_implicit_arg;
+
+/* Helper function for sort_section_numbers. */
+static int
+compare_section_numbers (const void *p1, const void *p2)
+{
+ DSO *dso = compare_section_numbers_implicit_arg;
+ const int i1 = *(const int *)p1;
+ const int i2 = *(const int *)p2;
+ GElf_Off o1;
+ GElf_Off o2;
+
+ /* Keep element 0 at 0. */
+ if (i1 == 0 || i2 == 0)
+ {
+ if (i1 == i2)
+ return 0;
+ if (i1 == 0)
+ return -1;
+ if (i2 == 0)
+ return 1;
+ }
+
+ /* Get file offsets. */
+ o1 = (i1 == dso->ehdr.e_shnum
+ ? dso->ehdr.e_shoff
+ : dso->shdr[i1].sh_offset);
+ o2 = (i2 == dso->ehdr.e_shnum
+ ? dso->ehdr.e_shoff
+ : dso->shdr[i2].sh_offset);
+
+ /* Compare file offsets. */
+ if (o1 < o2)
+ return -1;
+ if (o1 > o2)
+ return 1;
+
+ /* In case file offset is the same, keep the original relative order. */
+ if (i1 < i2)
+ return -1;
+ if (i1 > i2)
+ return 1;
+
+ return 0;
+}
+
+/* Sort SORTED_SECTION_NUMBERS in file offset order. */
+static void
+sort_section_numbers (DSO *dso, unsigned int *sorted_section_numbers)
+{
+ unsigned int i;
+ unsigned int nr_sections = dso->ehdr.e_shnum;
+
+ /* Treat section header table as another section, with index
+ dso->ehdr.e_shnum. */
+ nr_sections += 1;
+
+ for (i = 0; i < nr_sections; ++i)
+ sorted_section_numbers[i] = i;
+
+ compare_section_numbers_implicit_arg = dso;
+ qsort (sorted_section_numbers, nr_sections,
+ sizeof (sorted_section_numbers[0]), compare_section_numbers);
+ compare_section_numbers_implicit_arg = NULL;
+
+ assert (sorted_section_numbers[0] == 0);
+}
+
+/* Verify file offset and size of sections and section header table. */
+static void
+verify_sections (DSO *dso, unsigned int *sorted_section_numbers,
+ GElf_Off *distance, int addsec, GElf_Off addsize,
+ GElf_Ehdr ehdr)
+{
+ int i, j;
+ int prev, update_prev;
+ GElf_Off offset, prev_offset, prev_size;
+ GElf_Off section_header_table_size
+ = dso->ehdr.e_shentsize * ehdr.e_shnum;
+
+ prev = -1;
+ for (i = 1; i < (dso->ehdr.e_shnum + 1); ++i, prev = update_prev)
+ {
+ j = sorted_section_numbers[i];
+
+ if (j != dso->ehdr.e_shnum && dso->shdr[j].sh_type == SHT_NOBITS)
+ {
+ update_prev = prev;
+ continue;
+ }
+ update_prev = j;
+
+ if (prev == -1)
+ continue;
+
+ offset = (j == dso->ehdr.e_shnum
+ ? ehdr.e_shoff
+ : dso->shdr[j].sh_offset);
+
+ prev_offset = (prev == dso->ehdr.e_shnum
+ ? ehdr.e_shoff
+ : dso->shdr[prev].sh_offset);
+
+ prev_size = (prev == dso->ehdr.e_shnum
+ ? section_header_table_size
+ : (dso->shdr[prev].sh_type == SHT_NOBITS
+ ? 0
+ : dso->shdr[prev].sh_size));
+
+ if (distance != NULL)
+ assert ((prev_offset + prev_size + distance[prev]
+ + (prev == addsec ? addsize : 0))
+ == offset);
+ else
+ assert ((prev_offset + prev_size + (prev == addsec ? addsize : 0))
+ <= offset);
+ }
+}
+
+/* Calculate distance between sections and section header table. */
+static int
+calculate_section_distance (DSO *dso, unsigned int *sorted_section_numbers,
+ GElf_Off *distance)
+{
+ int i, j;
+ int prev, update_prev;
+ GElf_Off offset, prev_offset, prev_size;
+ GElf_Off section_header_table_size
+ = dso->ehdr.e_shentsize * dso->ehdr.e_shnum;
+
+ prev = -1;
+ for (i = 1; i < (dso->ehdr.e_shnum + 1); ++i, prev = update_prev)
+ {
+ j = sorted_section_numbers[i];
+
+ if (j != dso->ehdr.e_shnum && dso->shdr[j].sh_type == SHT_NOBITS)
+ {
+ update_prev = prev;
+ continue;
+ }
+ update_prev = j;
+
+ if (prev == -1)
+ continue;
+
+ offset = (j == dso->ehdr.e_shnum
+ ? dso->ehdr.e_shoff
+ : dso->shdr[j].sh_offset);
+
+ prev_offset = (prev == dso->ehdr.e_shnum
+ ? dso->ehdr.e_shoff
+ : dso->shdr[prev].sh_offset);
+
+ prev_size = (prev == dso->ehdr.e_shnum
+ ? section_header_table_size
+ : dso->shdr[prev].sh_size);
+
+ if (prev_offset + prev_size > offset)
+ {
+ error (0, 0, "Section overlap detected");
+ if (prev == dso->ehdr.e_shnum)
+ error (0, 0, "Section header table: [0x%llx, 0x%llx)",
+ (unsigned long long)prev_offset,
+ (unsigned long long)(prev_offset + prev_size));
+ else
+ error (0, 0, "Section %d: [0x%llx, 0x%llx)", j,
+ (unsigned long long)prev_offset,
+ (unsigned long long)(prev_offset + prev_size));
+ if (j == dso->ehdr.e_shnum)
+ error (0, 0, "Section header table: 0x%llx",
+ (unsigned long long)offset);
+ else
+ error (0, 0, "Section %d: 0x%llx", j, (unsigned long long)offset);
+ return 1;
+ }
+
+ distance[prev] = offset - (prev_offset + prev_size);
+ }
+
+ verify_sections (dso, sorted_section_numbers, distance, -1, 0, dso->ehdr);
+
+ return 0;
+}
+
+/* Store new ELF into FILE. debug_sections array contains
+ new_data/new_size pairs where needed. */
+static int
+write_dso (DSO *dso, const char *file, struct stat *st, bool save_to_temp)
+{
+ Elf *elf = NULL;
+ GElf_Ehdr ehdr;
+ GElf_Off min_shoff = ~(GElf_Off) 0;
+ char *e_ident;
+ int fd, i, j, addsec = -1, ret;
+ GElf_Off off, diff, addsize = 0;
+ char *filename = NULL;
+ GElf_Word shstrtabadd = 0;
+ char *shstrtab = NULL;
+ bool remove_sections[SECTION_COUNT];
+ GElf_Off distance[dso->ehdr.e_shnum + 1];
+ /* Array of sections and section header table sorted by file offset. */
+ unsigned int sorted_section_numbers[dso->ehdr.e_shnum + 1];
+ GElf_Off old_sh_offset[dso->ehdr.e_shnum];
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_dso\n");
+ }
+
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ old_sh_offset[i] = dso->shdr[i].sh_offset;
+
+ memset (remove_sections, '\0', sizeof (remove_sections));
+ ehdr = dso->ehdr;
+
+ sort_section_numbers (dso, sorted_section_numbers);
+ if (calculate_section_distance (dso, sorted_section_numbers, distance))
+ return 1;
+
+ for (i = 0; debug_sections[i].name; i++)
+ if (debug_sections[i].new_size != debug_sections[i].size)
+ {
+ if (debug_sections[i].size == 0
+ && debug_sections[i].sec == 0)
+ {
+ unsigned int len;
+ if (addsec == -1)
+ for (j = 0; debug_sections[j].name; j++)
+ if (debug_sections[j].new_size
+ && debug_sections[j].sec
+ && debug_sections[j].sec > addsec)
+ addsec = debug_sections[j].sec;
+ ehdr.e_shnum++;
+ if (ehdr.e_shoff < min_shoff)
+ min_shoff = ehdr.e_shoff;
+ for (j = 1; j < dso->ehdr.e_shnum; ++j)
+ {
+ if (dso->shdr[j].sh_offset > ehdr.e_shoff)
+ dso->shdr[j].sh_offset += ehdr.e_shentsize;
+ if (dso->shdr[j].sh_link > (unsigned int) addsec)
+ dso->shdr[j].sh_link++;
+ if ((dso->shdr[j].sh_type == SHT_REL
+ || dso->shdr[j].sh_type == SHT_RELA
+ || (dso->shdr[j].sh_flags & SHF_INFO_LINK))
+ && dso->shdr[j].sh_info
+ > (unsigned int) addsec)
+ dso->shdr[j].sh_info++;
+ }
+ if (dso->ehdr.e_shstrndx > addsec)
+ ehdr.e_shstrndx++;
+ len = strlen (debug_sections[i].name) + 1;
+ dso->shdr[dso->ehdr.e_shstrndx].sh_size += len;
+ if (dso->shdr[dso->ehdr.e_shstrndx].sh_offset < min_shoff)
+ min_shoff = dso->shdr[dso->ehdr.e_shstrndx].sh_offset;
+ for (j = 1; j < dso->ehdr.e_shnum; ++j)
+ if (dso->shdr[j].sh_offset
+ > dso->shdr[dso->ehdr.e_shstrndx].sh_offset)
+ dso->shdr[j].sh_offset += len;
+ if (ehdr.e_shoff > dso->shdr[dso->ehdr.e_shstrndx].sh_offset)
+ ehdr.e_shoff += len;
+ shstrtabadd += len;
+ diff = debug_sections[i].new_size;
+ addsize += diff;
+ off = dso->shdr[addsec].sh_offset;
+ }
+ else
+ {
+ diff = (GElf_Off) debug_sections[i].new_size
+ - (GElf_Off) dso->shdr[debug_sections[i].sec].sh_size;
+ off = dso->shdr[debug_sections[i].sec].sh_offset;
+ }
+ if (off < min_shoff)
+ min_shoff = off;
+ for (j = 1; j < dso->ehdr.e_shnum; ++j)
+ if (dso->shdr[j].sh_offset > off)
+ dso->shdr[j].sh_offset += diff;
+ if (ehdr.e_shoff > off)
+ ehdr.e_shoff += diff;
+ dso->shdr[debug_sections[i].sec].sh_size
+ = debug_sections[i].new_size;
+ if (debug_sections[i].new_size == 0)
+ {
+ remove_sections[i] = true;
+ ehdr.e_shnum--;
+ if (ehdr.e_shoff < min_shoff)
+ min_shoff = ehdr.e_shoff;
+ for (j = 1; j < dso->ehdr.e_shnum; ++j)
+ {
+ if (dso->shdr[j].sh_offset > ehdr.e_shoff)
+ dso->shdr[j].sh_offset -= ehdr.e_shentsize;
+ if (dso->shdr[j].sh_link
+ > (unsigned int) debug_sections[i].sec)
+ dso->shdr[j].sh_link--;
+ if ((dso->shdr[j].sh_type == SHT_REL
+ || dso->shdr[j].sh_type == SHT_RELA
+ || (dso->shdr[j].sh_flags & SHF_INFO_LINK))
+ && dso->shdr[j].sh_info
+ > (unsigned int) debug_sections[i].sec)
+ dso->shdr[j].sh_info--;
+ }
+ if (dso->ehdr.e_shstrndx > debug_sections[i].sec)
+ ehdr.e_shstrndx--;
+ }
+ }
+
+ /* Verify that we did not change section layout, by checking that the
+ distances between sections and section header table remained the same. */
+ verify_sections (dso, sorted_section_numbers, distance, addsec, addsize,
+ ehdr);
+
+ if (min_shoff != ~(GElf_Off) 0)
+ {
+ for (j = 1; j < dso->ehdr.e_shnum; ++j)
+ if (dso->shdr[j].sh_offset >= min_shoff
+ && dso->shdr[j].sh_addralign > 1
+ && (dso->shdr[j].sh_offset & (dso->shdr[j].sh_addralign - 1)) != 0)
+ break;
+ if (j < dso->ehdr.e_shnum
+ || (ehdr.e_shoff >= min_shoff
+ && (ehdr.e_shoff & (ehdr.e_ident[EI_CLASS] == ELFCLASS64
+ ? 7 : 3)) != 0))
+ {
+ /* Need to fix up sh_offset/e_shoff. Punt if all the sections
+ >= min_shoff aren't non-ALLOC. */
+ GElf_Off last_shoff = 0;
+ int k = -1;
+ int l;
+ for (l = 1; l <= dso->ehdr.e_shnum; ++l)
+ {
+ j = sorted_section_numbers[l];
+ if (j == dso->ehdr.e_shnum)
+ continue;
+ else if (!last_shoff
+ && (dso->shdr[j].sh_offset < min_shoff
+ || (dso->shdr[j].sh_offset == min_shoff
+ && (dso->shdr[j].sh_size == 0
+ || dso->shdr[j].sh_type == SHT_NOBITS))))
+ continue;
+ else if (dso->shdr[j].sh_type == SHT_NOBITS)
+ continue;
+ else if ((dso->shdr[j].sh_flags & SHF_ALLOC) != 0)
+ {
+ error (0, 0, "Allocatable section in %s after "
+ "non-allocatable ones", dso->filename);
+ return 1;
+ }
+ else
+ {
+ assert (dso->shdr[j].sh_offset >= last_shoff);
+
+ if (k == -1)
+ k = l;
+ last_shoff = dso->shdr[j].sh_offset + dso->shdr[j].sh_size;
+ }
+ }
+ last_shoff = min_shoff;
+ for (l = k; l <= dso->ehdr.e_shnum; ++l)
+ {
+ j = sorted_section_numbers[l];
+ if (j == dso->ehdr.e_shnum)
+ {
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ ehdr.e_shoff = (last_shoff + 7) & -8;
+ else
+ ehdr.e_shoff = (last_shoff + 3) & -4;
+ last_shoff = ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize;
+ continue;
+ }
+ /* Ignore SHT_NOBITS sections. */
+ if (dso->shdr[j].sh_type == SHT_NOBITS)
+ continue;
+ dso->shdr[j].sh_offset = last_shoff;
+ if (dso->shdr[j].sh_addralign > 1)
+ dso->shdr[j].sh_offset
+ = (last_shoff + dso->shdr[j].sh_addralign - 1)
+ & ~(dso->shdr[j].sh_addralign - (GElf_Off) 1);
+ last_shoff = dso->shdr[j].sh_offset + dso->shdr[j].sh_size;
+ if (addsec != -1 && j == addsec)
+ last_shoff += addsize;
+ }
+ }
+ }
+
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ if (dso->shdr[i].sh_type == SHT_NOBITS)
+ dso->shdr[i].sh_offset = old_sh_offset[i];
+
+ verify_sections (dso, sorted_section_numbers, NULL, addsec, addsize,
+ ehdr);
+
+ if (shstrtabadd != 0)
+ {
+ shstrtab = (char *) malloc (dso->shdr[dso->ehdr.e_shstrndx].sh_size);
+ if (shstrtab == NULL)
+ {
+ error (0, ENOMEM, "Failed to adjust .shstrtab for %s",
+ dso->filename);
+ return 1;
+ }
+ }
+
+ if (file == NULL)
+ {
+ size_t len = strlen (dso->filename);
+ filename = alloca (len + sizeof (".#dwz#.XXXXXX"));
+ memcpy (filename, dso->filename, len);
+ memcpy (filename + len, ".#dwz#.XXXXXX", sizeof (".#dwz#.XXXXXX"));
+ fd = mkstemp (filename);
+ file = (const char *) filename;
+ if (fd == -1)
+ {
+ error (0, errno, "Failed to create temporary file for %s",
+ dso->filename);
+ free (shstrtab);
+ return 1;
+ }
+ }
+ else
+ {
+ fd = open (file, O_RDWR | O_CREAT, 0600);
+ if (fd == -1)
+ {
+ error (0, errno, "Failed to open %s for writing", file);
+ free (shstrtab);
+ return 1;
+ }
+ }
+
+ elf = elf_begin (fd, ELF_C_WRITE, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
+ unlink (file);
+ close (fd);
+ free (shstrtab);
+ return 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, &ehdr) == 0
+ || gelf_newphdr (elf, ehdr.e_phnum) == 0)
+ {
+ error (0, 0, "Could not create new ELF headers");
+ unlink (file);
+ elf_end (elf);
+ close (fd);
+ free (shstrtab);
+ return 1;
+ }
+ 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 < dso->ehdr.e_shnum; ++i)
+ {
+ Elf_Scn *scn;
+ Elf_Data *data1, *data2;
+
+ for (j = 0; debug_sections[j].name; j++)
+ if (i == debug_sections[j].sec)
+ break;
+ if (debug_sections[j].name && remove_sections[j])
+ continue;
+ scn = elf_newscn (elf);
+ 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 (debug_sections[j].name
+ && debug_sections[j].new_data != NULL)
+ {
+ data2->d_buf = debug_sections[j].new_data;
+ data2->d_size = dso->shdr[i].sh_size;
+ }
+ if (i == dso->ehdr.e_shstrndx && shstrtabadd)
+ {
+ memcpy (shstrtab, data1->d_buf,
+ dso->shdr[dso->ehdr.e_shstrndx].sh_size
+ - shstrtabadd);
+ data2->d_buf = shstrtab;
+ data2->d_size = dso->shdr[i].sh_size;
+ }
+ if (i == addsec)
+ {
+ GElf_Word sh_name = dso->shdr[dso->ehdr.e_shstrndx].sh_size
+ - shstrtabadd;
+ GElf_Shdr shdr;
+
+ off = dso->shdr[i].sh_offset + dso->shdr[i].sh_size;
+ for (j = 0; debug_sections[j].name; j++)
+ if (debug_sections[j].new_size
+ && debug_sections[j].size == 0
+ && debug_sections[j].sec == 0)
+ {
+ scn = elf_newscn (elf);
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ memset (&shdr, '\0', sizeof (shdr));
+ shdr.sh_name = sh_name;
+ sh_name += strlen (debug_sections[j].name) + 1;
+ strcpy (shstrtab + shdr.sh_name, debug_sections[j].name);
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_offset = off;
+ shdr.sh_size = debug_sections[j].new_size;
+ shdr.sh_addralign = 1;
+ off += shdr.sh_size;
+ gelf_update_shdr (scn, &shdr);
+ data2 = elf_newdata (scn);
+ data2->d_buf = debug_sections[j].new_data;
+ data2->d_type = ELF_T_BYTE;
+ data2->d_version = EV_CURRENT;
+ data2->d_size = shdr.sh_size;
+ data2->d_off = 0;
+ data2->d_align = 1;
+ }
+ }
+ }
+
+ if (elf_update (elf, ELF_C_WRITE_MMAP) == -1)
+ {
+ error (0, 0, "%s: elf_update failed", dso->filename);
+ unlink (file);
+ elf_end (elf);
+ close (fd);
+ free (shstrtab);
+ return 1;
+ }
+
+ if (elf_end (elf) < 0)
+ {
+ error (0, 0, "elf_end failed: %s", elf_errmsg (elf_errno ()));
+ unlink (file);
+ elf_end (elf);
+ close (fd);
+ free (shstrtab);
+ return 1;
+ }
+
+ free (shstrtab);
+ ret = fchown (fd, st->st_uid, st->st_gid);
+ fchmod (fd, st->st_mode & 07777);
+ close (fd);
+
+ if (filename != NULL && rename (filename, dso->filename))
+ {
+ error (0, errno, "Failed to rename temporary file over %s",
+ dso->filename);
+ unlink (file);
+ /* | (ret & 1) to silence up __wur warning for fchown. */
+ return 1 | (ret & 1);
+ }
+ if (save_to_temp)
+ {
+ const char *prefix = "dwz.";
+ size_t buf_len = strlen (prefix) + strlen (dso->filename) + 1;
+ char *buf = (char *)alloca (buf_len);
+ size_t offset = 0;
+ strcpy (&buf[offset], prefix);
+ offset += strlen (prefix);
+ strcpy (&buf[offset], dso->filename);
+ offset += strlen (dso->filename);
+ assert (offset == buf_len - 1);
+ assert (buf[offset] == '\0');
+ unlink (buf);
+ if (link (dso->filename, buf) != 0)
+ {
+ error (0, errno, "Failed to link file: %s", dso->filename);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Free memory and clear global variables. */
+static void
+cleanup (void)
+{
+ dw_cu_ref cu;
+ unsigned int i;
+
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ {
+ if (cu->cu_new_abbrev)
+ htab_delete (cu->cu_new_abbrev);
+ cu->cu_new_abbrev = NULL;
+ }
+ if (off_htab != NULL)
+ {
+ if (tracing)
+ htab_report (off_htab, "off_htab final");
+ htab_delete (off_htab);
+ }
+ off_htab = NULL;
+ if (types_off_htab != NULL)
+ htab_delete (types_off_htab);
+ types_off_htab = NULL;
+ if (loc_htab != NULL)
+ htab_delete (loc_htab);
+ loc_htab = NULL;
+ if (loclists_htab != NULL)
+ htab_delete (loclists_htab);
+ loclists_htab = NULL;
+ if (dup_htab != NULL)
+ htab_delete (dup_htab);
+ dup_htab = NULL;
+ if (strp_htab != NULL)
+ htab_delete (strp_htab);
+ strp_htab = NULL;
+ if (line_htab != NULL)
+ htab_delete (line_htab);
+ line_htab = NULL;
+ if (macro_htab != NULL)
+ htab_delete (macro_htab);
+ macro_htab = NULL;
+ if (meta_abbrev_htab != NULL)
+ htab_delete (meta_abbrev_htab);
+ meta_abbrev_htab = NULL;
+
+ for (i = 0; i < SAVED_SECTIONS; ++i)
+ {
+ free (saved_new_data[i]);
+ saved_new_data[i] = NULL;
+ }
+
+ obstack_free (&ob2, NULL);
+ obstack_free (&ob, NULL);
+ memset (&ob2, '\0', sizeof (ob2));
+ memset (&ob, '\0', sizeof (ob2));
+ die_nontoplevel_freelist = NULL;
+ die_collapsed_child_freelist = NULL;
+ pool_destroy (NULL);
+ first_cu = NULL;
+ last_cu = NULL;
+ ptr_size = 0;
+ max_nattr = 0;
+ do_read_16 = NULL;
+ do_read_32 = NULL;
+ do_read_64 = NULL;
+ do_write_16 = NULL;
+ do_write_32 = NULL;
+ do_write_64 = NULL;
+ edge_freelist = NULL;
+ multifile_mode = 0;
+ max_strp_off = 0;
+ max_line_id = 0;
+}
+
+/* Propagate the die_no_multifile property along the duplicate chain of which
+ DIE is a member. If the property was changed on any die, set *CHANGED to
+ true. */
+static void
+propagate_multifile_duplicate_chain (dw_die_ref die, bool *changed)
+{
+ dw_die_ref dup = first_dup (die);
+ if (!dup)
+ return;
+
+ while (dup && dup->die_offset == -1U)
+ dup = dup->die_nextdup;
+ if (dup != die)
+ return;
+
+ bool any_no_multifile = false;
+ bool any_multifile = false;
+ bool prop_needed = false;
+ dw_die_ref d;
+ for (d = dup; d && !prop_needed; d = d->die_nextdup)
+ {
+ if (d->die_no_multifile)
+ any_no_multifile = true;
+ else
+ any_multifile = true;
+ prop_needed = any_no_multifile && any_multifile;
+ }
+ if (!prop_needed)
+ return;
+
+ *changed = true;
+
+ for (d = dup; d; d = d->die_nextdup)
+ d->die_no_multifile = 1;
+}
+
+/* Propagate the die_no_multifile property backwards along the outgoing
+ references of DIE, which is a member of CU and of the subtree of lower
+ toplevel die TOP_DIE. If the property was changed on any die, set *CHANGED
+ to true. */
+static void
+propagate_multifile_refs_backward (dw_cu_ref cu, dw_die_ref top_die,
+ dw_die_ref die, bool *changed)
+{
+ struct abbrev_tag *t = die->die_abbrev;
+ unsigned int i;
+ unsigned char *ptr;
+ dw_die_ref child;
+
+ if (die->die_offset == -1U)
+ return;
+
+ ptr = debug_sections[DEBUG_INFO].data + die->die_offset;
+ skip_leb128 (ptr);
+ for (i = 0; i < t->nattr; ++i)
+ {
+ uint32_t form = t->attr[i].form;
+ uint64_t value;
+ dw_die_ref ref, reft;
+
+ while (form == DW_FORM_indirect)
+ form = read_uleb128 (ptr);
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ value = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4);
+ ptr += cu->cu_version == 2 ? ptr_size : 4;
+ ref = off_htab_lookup (cu, value);
+ goto finish_ref;
+ 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)
+ break;
+ ref = off_htab_lookup (cu, cu->cu_offset + value);
+ finish_ref:
+ reft = ref;
+ while (!reft->die_root
+ && 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_root)
+ ;
+ else if (reft->die_ck_state == CK_KNOWN
+ && !top_die->die_no_multifile && reft->die_no_multifile)
+ {
+ top_die->die_no_multifile = 1;
+ *changed = true;
+ }
+ break;
+ default:
+ ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr);
+ }
+ }
+
+ for (child = die->die_child; child; child = child->die_sib)
+ propagate_multifile_refs_backward (cu, top_die, child, changed);
+}
+
+/* Do propagation of the die_no_multifile property that was not covered in
+ checksum_die and checksum_ref_die. */
+static void
+propagate_multifile (void)
+{
+ bool changed;
+ dw_cu_ref cu;
+ dw_die_ref die;
+
+ changed = false;
+
+ FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die)
+ propagate_multifile_duplicate_chain (die, &changed);
+
+ if (!changed)
+ return;
+
+ do
+ {
+ changed = false;
+
+ FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die)
+ propagate_multifile_refs_backward (cu, die, die, &changed);
+
+ FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die)
+ propagate_multifile_duplicate_chain (die, &changed);
+ }
+ while (changed);
+}
+
+/* Returns true if DIE contains any toplevel children that can be
+ potentially shared between different executables or shared libraries. */
+static bool
+check_multifile (dw_die_ref die)
+{
+ dw_die_ref child;
+
+ die->die_no_multifile = 1;
+ for (child = die->die_child; child; child = child->die_sib)
+ if (child->die_named_namespace)
+ {
+ if (check_multifile (child))
+ die->die_no_multifile = 0;
+ }
+ else if (child->die_offset == -1U)
+ {
+ if (child->die_nextdup && child->die_nextdup->die_dup == child)
+ {
+ if (child->die_nextdup->die_ck_state == CK_KNOWN
+ && child->die_nextdup->die_no_multifile == 0)
+ {
+ child->die_no_multifile = 0;
+ die->die_no_multifile = 0;
+ }
+ else
+ child->die_no_multifile = 1;
+ }
+ else
+ child->die_no_multifile = 1;
+ }
+ else
+ {
+ child->die_op_type_referenced = 0;
+ if (child->die_dup == NULL
+ && child->die_ck_state == CK_KNOWN
+ && child->die_no_multifile == 0)
+ die->die_no_multifile = 0;
+ else
+ child->die_no_multifile = 1;
+ }
+ return die->die_no_multifile == 0;
+}
+
+/* Helper function for write_multifile_strp to sort strp_entry
+ by increasing new_off. */
+static int
+strp_off_cmp (const void *p, const void *q)
+{
+ struct strp_entry *s1 = *(struct strp_entry **)p;
+ struct strp_entry *s2 = *(struct strp_entry **)q;
+ if (s1->new_off < s2->new_off)
+ return -1;
+ if (s1->new_off > s2->new_off)
+ return 1;
+ return 0;
+}
+
+/* Write tail optimized strings into the temporary .debug_str file. */
+static int
+write_multifile_strp (void)
+{
+ unsigned int count = htab_elements (strp_htab), i, buf_alloc, buf_size;
+ struct strp_entry **arr = (struct strp_entry **)
+ obstack_alloc (&ob, count * sizeof (*arr));
+ struct strp_entry **end = arr;
+ unsigned char *buf;
+ int ret = 0;
+
+ htab_traverse (strp_htab, list_strp_entries, (void *) &end);
+ assert (arr + count == end);
+ qsort (arr, count, sizeof (struct strp_entry *), strp_off_cmp);
+ buf_alloc = max_strp_off - debug_sections[DEBUG_STR].size;
+ if (buf_alloc > 131072)
+ buf_alloc = 131072;
+ buf = (unsigned char *) obstack_alloc (&ob, buf_alloc);
+ buf_size = 0;
+ for (i = 0; i < count; i++)
+ {
+ unsigned char *p = debug_sections[DEBUG_STR].data + arr[i]->off;
+ unsigned int len = strlen ((char *) p) + 1;
+ if (buf_alloc - buf_size < len)
+ {
+ if (buf_size
+ && write (multi_str_fd, buf, buf_size) != (ssize_t) buf_size)
+ {
+ ret = 1;
+ break;
+ }
+ buf_size = 0;
+ if (buf_alloc < len)
+ {
+ if (write (multi_str_fd, p, len) != (ssize_t) len)
+ {
+ ret = 1;
+ break;
+ }
+ continue;
+ }
+ }
+ memcpy (buf + buf_size, p, len);
+ buf_size += len;
+ }
+ if (buf_size
+ && ret == 0
+ && write (multi_str_fd, buf, buf_size) != (ssize_t) buf_size)
+ ret = 1;
+ obstack_free (&ob, (void *) arr);
+ return ret;
+}
+
+/* Hold some statistics on the line entries so we know whether to emit
+ time and/or sizes. Used by list_line_entries used by
+ write_multifile_line. */
+struct line_stats
+{
+ struct line_entry ***end;
+ bool has_time;
+ bool has_size;
+};
+
+/* Helper to find the end of the line_htab entries and other stats.
+ Called through htab_traverse. */
+static int
+list_line_entries (void **slot, void *data)
+{
+ struct line_stats *stats = (struct line_stats *) data;
+ struct line_entry *entry = (struct line_entry *) *slot;
+ struct line_entry ***end = stats->end;
+ **end = entry;
+ (*end)++;
+ if (entry->file->time != 0)
+ stats->has_time = true;
+ if (entry->file->size != 0)
+ stats->has_size = true;
+ return 1;
+}
+
+/* Helper function for write_multifile_strp to sort line_entry
+ by increasing new_id. */
+static int
+line_id_cmp (const void *p, const void *q)
+{
+ struct line_entry *s1 = *(struct line_entry **)p;
+ struct line_entry *s2 = *(struct line_entry **)q;
+ if (s1->new_id < s2->new_id)
+ return -1;
+ if (s1->new_id > s2->new_id)
+ return 1;
+ return 0;
+}
+
+/* Write a minimal .debug_line entry. If not op_multifile, write it
+ into the temporary .debug_line file (if line_htab is non-NULL, fill
+ its directory and file table from it, otherwise emit an entry
+ with no directories or files), if op_multifile, store the entry
+ into debug_sections[DEBUG_LINE].new_data which it allocates. */
+static int
+write_multifile_line (void)
+{
+ unsigned int filecnt = 0, dircnt = 0, filetbllen = 0, dirtbllen = 0;
+ unsigned int header_len, len, i, j;
+ unsigned char *line, *ptr;
+ struct line_entry **filearr = NULL;
+ struct line_stats line_stats;
+ unsigned int *diridx = NULL, *dirarr = NULL;
+ unsigned char buf[45]; /* Max header_len, see below. */
+ int ret = 0;
+
+ line_stats.has_time = line_stats.has_size = false;
+ if (line_htab)
+ {
+ struct line_entry **end;
+ filecnt = htab_elements (line_htab);
+ filearr = (struct line_entry **)
+ obstack_alloc (&ob, filecnt * sizeof (*filearr));
+ end = filearr;
+ line_stats.end = &end;
+ htab_traverse (line_htab, list_line_entries, (void *) &line_stats);
+ assert (filearr + filecnt == end);
+ diridx = (unsigned int *)
+ obstack_alloc (&ob, filecnt * sizeof (*diridx));
+ qsort (filearr, filecnt, sizeof (struct line_entry *), line_id_cmp);
+ for (i = 0; i < filecnt; i++)
+ {
+ unsigned int direntrylen = 0;
+ const char *file = filearr[i]->file->file;
+ if (filearr[i]->file->dir == NULL)
+ {
+ const char *r = strrchr (file, '/'), *s;
+
+ j = 0;
+ direntrylen = r ? r - file : 0;
+ while (direntrylen && file[direntrylen - 1] == '/')
+ direntrylen--;
+ if (direntrylen)
+ {
+ direntrylen++;
+ for (j = 0; j < dircnt; j++)
+ if (filearr[dirarr[j]]->file->dir == NULL
+ && strncmp (filearr[dirarr[j]]->file->file,
+ file, direntrylen) == 0)
+ {
+ s = filearr[dirarr[j]]->file->file + direntrylen;
+ while (*s == '/')
+ s++;
+ if (strchr (s, '/'))
+ continue;
+ break;
+ }
+ j++;
+ file = r + 1;
+ }
+ }
+ else
+ {
+ for (j = 0; j < dircnt; j++)
+ if (filearr[dirarr[j]]->file->dir
+ && strcmp (filearr[dirarr[j]]->file->dir,
+ filearr[i]->file->dir) == 0)
+ break;
+ j++;
+ direntrylen = strlen (filearr[i]->file->dir) + 1;
+ }
+ if (j <= dircnt)
+ diridx[i] = j;
+ else
+ {
+ obstack_int_grow (&ob, i);
+ diridx[i] = ++dircnt;
+ dirarr = (unsigned int *) obstack_base (&ob);
+ dirtbllen += direntrylen;
+ }
+ filetbllen += strlen (file) + 1;
+ filetbllen += size_of_uleb128 (diridx[i]);
+ if (lowest_line_version < 5 || line_stats.has_time)
+ filetbllen += size_of_uleb128 (filearr[i]->file->time);
+ if (lowest_line_version < 5 || line_stats.has_size)
+ filetbllen += size_of_uleb128 (filearr[i]->file->size);
+ }
+ dirarr = (unsigned int *) obstack_finish (&ob);
+ }
+
+ /* standard .debug_line "header" length (both version 2 and 5):
+ unit_length (4) + version (2) + header_length (4) +
+ min_instr_length (1) + default_is_stmt (1) + line_base (1) +
+ line_range (1) + opcode_base (1) = 15
+
+ version 2 adds 2 bytes, one zero byte to terminate dir and file lists.
+
+ version 5 adds at least 11 bytes, max_ops_per_instr (1) +
+ address_size (1) + segment_size (1) + dir_entry_format_cnt (1) +
+ format_pair (2), file_entry_format_cnt (1) + file_format_pairs
+ (4). Plus dircnt (uleb128) + format_pair (2) if has_time +
+ format_pair (2) if has_size) + filecnt (uleb128).
+
+ version 5 also has 2 extra 6 byte "<dwz>" string entries for dir
+ and file entry zero, plus one for the zero file entry dir idx.
+ */
+ header_len = 15;
+ if (lowest_line_version < 5)
+ header_len += 2;
+ else
+ {
+ header_len += 11;
+ header_len += size_of_uleb128 (dircnt + 1);
+ header_len += size_of_uleb128 (filecnt + 1);
+ if (line_stats.has_time)
+ header_len += 2;
+ if (line_stats.has_size)
+ header_len += 2;
+ header_len += 2 * 6 + 1;
+ }
+ len = header_len + filetbllen + dirtbllen;
+ if (unlikely (op_multifile))
+ {
+ debug_sections[DEBUG_LINE].new_size = len;
+ debug_sections[DEBUG_LINE].new_data = malloc (len);
+ if (debug_sections[DEBUG_LINE].new_data == NULL)
+ dwz_oom ();
+ line = debug_sections[DEBUG_LINE].new_data;
+ }
+ else
+ {
+ if (multi_line_off + len < multi_line_off)
+ {
+ if (line_htab)
+ obstack_free (&ob, (void *) filearr);
+ return 1;
+ }
+
+ if (len == header_len)
+ {
+ line = buf;
+ assert (sizeof (buf) >= header_len);
+ }
+ else
+ line = (unsigned char *) obstack_alloc (&ob, len);
+ }
+ ptr = line;
+ write_32 (ptr, len - 4); /* Total length. */
+ if (lowest_line_version < 5)
+ write_16 (ptr, 2); /* DWARF version. */
+ else
+ {
+ write_16 (ptr, 5); /* DWARF version. */
+ write_8 (ptr, multi_ptr_size); /* Address size. */
+ write_8 (ptr, 0); /* Segment size. */
+ }
+ write_32 (ptr, /* Header length. */
+ len - (lowest_line_version < 5 ? 10 : 12));
+ write_8 (ptr, 1); /* Minimum insn length. */
+ if (lowest_line_version >= 5)
+ write_8 (ptr, 1); /* Maximum ops per instr. */
+ write_8 (ptr, 1); /* Default is_stmt. */
+ write_8 (ptr, 0); /* Line base. */
+ write_8 (ptr, 1); /* Line range. */
+ write_8 (ptr, 1); /* Opcode base. */
+
+ if (lowest_line_version >= 5)
+ {
+ write_8 (ptr, 1); /* Dir entry format count. */
+ write_uleb128 (ptr, DW_LNCT_path);
+ write_uleb128 (ptr, DW_FORM_string);
+ write_uleb128 (ptr, dircnt + 1); /* Dir cnt. */
+ memcpy (ptr, "<dwz>", 6); /* Zero entry empty dir path. */
+ ptr += 6;
+ }
+
+ for (i = 0; i < dircnt; i++)
+ {
+ unsigned int l;
+ if (filearr[dirarr[i]]->file->dir)
+ {
+ l = strlen (filearr[dirarr[i]]->file->dir) + 1;
+ memcpy (ptr, filearr[dirarr[i]]->file->dir, l);
+ }
+ else
+ {
+ const char *file = filearr[dirarr[i]]->file->file;
+ const char *r = strrchr (file, '/');
+
+ while (r && r > file && r[-1] == '/')
+ r--;
+ l = r - file + 1;
+ memcpy (ptr, file, l - 1);
+ ptr[l - 1] = '\0';
+ }
+ ptr += l;
+ }
+ if (lowest_line_version < 5)
+ write_8 (ptr, 0); /* Terminate dir table. */
+ else
+ {
+ unsigned int format_cnt = 2 + line_stats.has_size + line_stats.has_time;
+ write_8 (ptr, format_cnt); /* File entry format count. */
+ write_uleb128 (ptr, DW_LNCT_path);
+ write_uleb128 (ptr, DW_FORM_string);
+ write_uleb128 (ptr, DW_LNCT_directory_index);
+ write_uleb128 (ptr, DW_FORM_udata);
+ if (line_stats.has_time)
+ {
+ write_uleb128 (ptr, DW_LNCT_timestamp);
+ write_uleb128 (ptr, DW_FORM_udata);
+ }
+ if (line_stats.has_size)
+ {
+ write_uleb128 (ptr, DW_LNCT_size);
+ write_uleb128 (ptr, DW_FORM_udata);
+ }
+ write_uleb128 (ptr, filecnt + 1); /* File names cnt. */
+ memcpy (ptr, "<dwz>", 6); /* Zero entry empty file path. */
+ ptr += 6;
+ write_8 (ptr, 0); /* Zero entry zero diridx. */
+ if (line_stats.has_time)
+ write_8 (ptr, 0);
+ if (line_stats.has_size)
+ write_8 (ptr, 0);
+ }
+
+ for (i = 0; i < filecnt; i++)
+ {
+ const char *file = filearr[i]->file->file;
+ unsigned int l;
+ if (diridx[i] && filearr[i]->file->dir == NULL)
+ file = strrchr (file, '/') + 1;
+ l = strlen (file) + 1;
+ memcpy (ptr, file, l);
+ ptr += l;
+ write_uleb128 (ptr, diridx[i]);
+ if (lowest_line_version < 5 || line_stats.has_time)
+ write_uleb128 (ptr, filearr[i]->file->time);
+ if (lowest_line_version < 5 || line_stats.has_size)
+ write_uleb128 (ptr, filearr[i]->file->size);
+ }
+ if (lowest_line_version < 5)
+ write_8 (ptr, 0); /* Terminate file table. */
+ assert (ptr == line + len);
+
+ if (likely (!op_multifile))
+ {
+ if (write (multi_line_fd, line, len) != (ssize_t) len)
+ ret = 1;
+ else
+ multi_line_off += len;
+ if (line_htab)
+ obstack_free (&ob, (void *) filearr);
+ else if (line != buf)
+ obstack_free (&ob, (void *) line);
+ }
+ else if (line_htab)
+ obstack_free (&ob, (void *) filearr);
+ return ret;
+}
+
+#if DEVEL
+/* In struct dw_die we have a union u with fields p1 and p2. The p1 field is
+ used during phase 1, after which the space is reused for the p2 field
+ during phase 2. Clear the p2 field to get rid of values stored to p1
+ during phase 1. */
+static int
+clear_p2_field (void)
+{
+ dw_cu_ref cu;
+ dw_die_ref die;
+
+ FOREACH_DIE (cu, die)
+ {
+ assert (die->die_collapsed_child == 0);
+ die->u.p2.die_new_abbrev = NULL;
+ die->u.p2.die_new_offset = 0;
+ die->u.p2.die_intracu_udata_size = 0;
+ }
+
+ return 0;
+}
+#endif
+
+/* Helper structure for file state. */
+struct file_result
+{
+ /* -3: Uninitialized.
+ -2: Already processed under different name.
+ -1: Ignore.
+ 0: Processed, changed.
+ 1: Processed, unchanged. */
+ int res;
+ int ret;
+ size_t hardlink_to;
+ unsigned int die_count;
+ bool skip_multifile;
+ bool low_mem_p;
+};
+
+/* Collect potentially shareable DIEs, strings and .debug_macro
+ opcode sequences into temporary .debug_* files. */
+static int
+write_multifile_1 (DSO *dso, struct file_result *res)
+{
+ dw_cu_ref cu;
+ bool any_cus = false;
+ unsigned int i;
+ int ret = 0;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_multifile\n");
+ }
+
+ if (multi_ehdr.e_ident[0] == '\0')
+ multi_ehdr = dso->ehdr;
+
+ if (multifile_force_ptr_size && ptr_size != multifile_force_ptr_size)
+ {
+ error (0, 0, "File %s skipped for multi-file optimization, different"
+ " pointer size", dso->filename);
+ res->skip_multifile = true;
+ return 1;
+ }
+ else if (multi_ptr_size == 0)
+ multi_ptr_size = ptr_size;
+ else if (ptr_size != multi_ptr_size)
+ {
+ error (0, 0, "Multi-file optimization not allowed for different"
+ " pointer sizes");
+ multifile = NULL;
+ return 1;
+ }
+ else
+ {
+ /* Same ptr_size. */
+ }
+
+ int endianity = (do_read_32 == buf_read_ule32
+ ? ELFDATA2LSB
+ : ELFDATA2MSB);
+ if (multifile_force_endian && endianity != multifile_force_endian)
+ {
+ error (0, 0, "File %s skipped for multi-file optimization, different"
+ " endianity", dso->filename);
+ res->skip_multifile = true;
+ return 1;
+ }
+ else if (multi_endian == 0)
+ multi_endian = endianity;
+ else if (multi_endian != endianity)
+ {
+ error (0, 0, "Multi-file optimization not allowed for different"
+ " endianity");
+ multifile = NULL;
+ return 1;
+ }
+ else
+ {
+ /* Same endianity. */
+ }
+
+#if DEVEL
+ clear_p2_field ();
+#endif
+
+ for (i = 0; i < SAVED_SECTIONS; i++)
+ {
+ saved_new_data[i] = debug_sections[i].new_data;
+ saved_new_size[i] = debug_sections[i].new_size;
+ debug_sections[i].new_data = NULL;
+ debug_sections[i].new_size = debug_sections[i].size;
+ }
+ propagate_multifile ();
+ for (cu = first_cu; cu && cu->cu_kind != CU_TYPES; cu = cu->cu_next)
+ {
+ cu->u1.cu_new_abbrev_owner = NULL;
+ cu->u2.cu_new_abbrev_offset = 0;
+ cu->cu_new_offset = 0;
+ any_cus |= check_multifile (cu->cu_die);
+ }
+ if (any_cus)
+ {
+ dw_cu_ref *cup;
+
+ for (cup = &first_cu; *cup && (*cup)->cu_kind != CU_TYPES; )
+ {
+ if ((*cup)->cu_new_abbrev)
+ htab_delete ((*cup)->cu_new_abbrev);
+
+ if ((*cup)->cu_die->die_no_multifile == 0)
+ cup = &(*cup)->cu_next;
+ else
+ *cup = (*cup)->cu_next;
+ }
+ *cup = NULL;
+ multifile_mode = MULTIFILE_MODE_WR;
+ if (tracing)
+ fprintf (stderr, "Write-multifile %s\n", dso->filename);
+ if (compute_abbrevs (NULL))
+ ret = 1;
+ else if (debug_sections[DEBUG_MACRO].data && read_macro (dso))
+ ret = 1;
+ else if ((unsigned int) (multi_info_off
+ + debug_sections[DEBUG_INFO].new_size)
+ < multi_info_off
+ || (unsigned int) (multi_abbrev_off
+ + debug_sections[DEBUG_ABBREV].new_size)
+ < multi_abbrev_off
+ || (unsigned int) (multi_str_off
+ + (max_strp_off ? max_strp_off
+ : debug_sections[DEBUG_ABBREV].size))
+ < multi_str_off
+ || (unsigned int) (multi_macro_off
+ + debug_sections[DEBUG_MACRO].new_size)
+ < multi_macro_off)
+ {
+ error (0, 0, "Multifile temporary files too large");
+ multifile = NULL;
+ ret = 1;
+ }
+ else
+ {
+ const char *mfile;
+ write_abbrev ();
+ write_info (NULL);
+ /* Any error in this is fatal for multifile handling of further
+ files. */
+ mfile = multifile;
+ multifile = NULL;
+ if (write (multi_abbrev_fd, debug_sections[DEBUG_ABBREV].new_data,
+ debug_sections[DEBUG_ABBREV].new_size)
+ != (ssize_t) debug_sections[DEBUG_ABBREV].new_size
+ || write (multi_info_fd, debug_sections[DEBUG_INFO].new_data,
+ debug_sections[DEBUG_INFO].new_size)
+ != (ssize_t) debug_sections[DEBUG_INFO].new_size
+ || write (multi_str_fd, debug_sections[DEBUG_STR].data,
+ debug_sections[DEBUG_STR].size)
+ != (ssize_t) debug_sections[DEBUG_STR].size
+ || (debug_sections[DEBUG_MACRO].new_data
+ && write (multi_macro_fd,
+ debug_sections[DEBUG_MACRO].new_data,
+ debug_sections[DEBUG_MACRO].new_size)
+ != (ssize_t) debug_sections[DEBUG_MACRO].new_size)
+ || (strp_htab != NULL && write_multifile_strp ())
+ || write_multifile_line ())
+ {
+ error (0, 0, "Error writing multi-file temporary files");
+ ret = 1;
+ }
+ else
+ {
+ multi_info_off += debug_sections[DEBUG_INFO].new_size;
+ multi_abbrev_off += debug_sections[DEBUG_ABBREV].new_size;
+ multi_str_off += max_strp_off ? max_strp_off
+ : debug_sections[DEBUG_STR].size;
+ multi_macro_off += debug_sections[DEBUG_MACRO].new_size;
+ multifile = mfile;
+ }
+ }
+ }
+ multifile_mode = 0;
+ for (i = 0; i < SAVED_SECTIONS; i++)
+ {
+ free (debug_sections[i].new_data);
+ debug_sections[i].new_data = saved_new_data[i];
+ debug_sections[i].new_size = saved_new_size[i];
+ saved_new_data[i] = NULL;
+ }
+ return ret;
+}
+
+struct pipe
+{
+ int readfd;
+ int writefd;
+};
+
+static bool write_multifile_parallel_p;
+static int child_id;
+static struct pipe *pipes;
+
+/* Get token. */
+static void
+get_token (void)
+{
+ int n = child_id;
+ int readfd = pipes[n].readfd;
+ int writefd = pipes[n].writefd;
+ close (writefd);
+ char buf;
+ read (readfd, &buf, 1);
+ close (readfd);
+}
+
+/* Pass token to child N. */
+static void
+pass_token (int n)
+{
+ int readfd = pipes[n].readfd;
+ int writefd = pipes[n].writefd;
+ close (readfd);
+ char buf = '\0';
+ write (writefd, &buf, 1);
+ close (writefd);
+}
+
+/* Wrapper around write_multifile_1 that ensures write_multifile_1 is called
+ in file order. */
+static int
+write_multifile (DSO *dso, struct file_result *res)
+{
+ int ret;
+
+ if (write_multifile_parallel_p)
+ {
+ get_token ();
+
+ multi_info_off = lseek (multi_info_fd, 0L, SEEK_END);
+ multi_abbrev_off = lseek (multi_abbrev_fd, 0L, SEEK_END);
+ multi_line_off = lseek (multi_line_fd, 0L, SEEK_END);
+ multi_str_off = lseek (multi_str_fd, 0L, SEEK_END);
+ multi_macro_off = lseek (multi_macro_fd, 0L, SEEK_END);
+ }
+
+ ret = write_multifile_1 (dso, res);
+
+ return ret;
+}
+
+/* During fi_multifile phase, see what DIEs in a partial unit
+ contain no children worth keeping where all real DIEs have
+ dups in the shared .debug_info section and what remains is
+ just the DW_TAG_partial_unit, a single DW_TAG_imported_unit
+ and perhaps some empty named namespaces. Then all the
+ references to that partial unit can be replaced by references
+ to the shared partial unit DW_TAG_import_unit has been importing. */
+static bool
+remove_empty_pu (dw_die_ref die)
+{
+ dw_die_ref child = die->die_child, dup = NULL;
+ if (!die->die_named_namespace)
+ {
+ if (die->die_tag != DW_TAG_partial_unit
+ || child == NULL
+ || child->die_tag != DW_TAG_imported_unit
+ || child->die_offset != -1U)
+ return false;
+ if (die->die_abbrev->nattr > 2)
+ return false;
+ if (die->die_abbrev->nattr
+ && die->die_abbrev->attr[0].attr != DW_AT_stmt_list)
+ return false;
+ if (die->die_abbrev->nattr == 2
+ && die->die_abbrev->attr[1].attr != DW_AT_comp_dir)
+ return false;
+ dup = child->die_nextdup;
+ child = child->die_sib;
+ }
+ else
+ {
+ if (die->die_abbrev->nattr > 2)
+ return false;
+ if (die->die_abbrev->nattr
+ && die->die_abbrev->attr[0].attr != DW_AT_name)
+ return false;
+ if (die->die_abbrev->nattr == 2
+ && die->die_abbrev->attr[1].attr != DW_AT_sibling)
+ return false;
+ }
+ for (; child; child = child->die_sib)
+ if (!child->die_named_namespace)
+ {
+ if (!child->die_remove)
+ /* Signal that DIE can't be removed, but
+ perhaps we could still remove_empty_pu
+ some named namespaces that are children of DIE. */
+ dup = die;
+ if (dup == NULL && die->die_named_namespace)
+ dup = child->die_dup->die_parent;
+ }
+ else if (!remove_empty_pu (child))
+ return false;
+ else if (dup == NULL && die->die_named_namespace)
+ dup = child->die_dup->die_parent;
+ if (dup == NULL || dup == die)
+ return false;
+ die->die_remove = 1;
+ assert (dup->die_tag == die->die_tag);
+ die->die_dup = dup;
+ return true;
+}
+
+/* Call remove_empty_pu on all partial units. */
+static int
+remove_empty_pus (void)
+{
+ dw_cu_ref cu;
+ for (cu = first_cu; cu; cu = cu->cu_next)
+ if (cu->cu_kind == CU_NORMAL
+ && cu->cu_die->die_tag == DW_TAG_partial_unit)
+ remove_empty_pu (cu->cu_die);
+ return 0;
+}
+
+/* Handle compression of a single file FILE. If OUTFILE is
+ non-NULL, the result will be stored into that file, otherwise
+ the result will be written into a temporary file that is renamed
+ over FILE. */
+static int
+dwz (const char *file, const char *outfile, struct file_result *res)
+{
+ DSO *dso;
+ int ret = 0, fd;
+ unsigned int i;
+ struct stat st;
+
+ if (res->res == -1)
+ return 1;
+
+ res->res = -1;
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ {
+ error (0, errno, "Failed to open input file %s", file);
+ return 1;
+ }
+ if (fstat (fd, &st) < 0)
+ {
+ close (fd);
+ error (0, errno, "Failed to stat input file %s", file);
+ return 1;
+ }
+
+ res->res = 1;
+
+ if (tracing)
+ {
+ fprintf (stderr, "Compressing %s", file);
+ if (multifile_mode == 0)
+ ;
+ else if (low_mem)
+ fprintf (stderr, " in low-mem mode");
+ else if (fi_multifile)
+ fprintf (stderr, " in finalize-multifile mode");
+ else
+ abort ();
+ fprintf (stderr, "\n");
+ }
+
+ dso = fdopen_dso (fd, file);
+ if (dso == NULL)
+ return 1;
+
+ obstack_alloc_failed_handler = dwz_oom;
+ if (setjmp (oom_buf))
+ {
+ error (0, ENOMEM, "%s: Could not allocate memory", dso->filename);
+
+ cleanup ();
+ ret = 1;
+ }
+ else
+ {
+ obstack_init (&ob);
+ obstack_init (&ob2);
+
+ unsigned int *die_count = multifile ? &res->die_count : NULL;
+ ret = read_dwarf (dso, quiet && outfile == NULL, die_count);
+ if (ret)
+ cleanup ();
+ else if (partition_dups ()
+ || create_import_tree ()
+ || (unlikely (fi_multifile)
+ && (remove_empty_pus ()
+ || read_macro (dso)))
+ || read_debug_info (dso, DEBUG_TYPES, NULL)
+#if DEVEL
+ || clear_p2_field ()
+#endif
+ || compute_abbrevs (dso)
+ || (unlikely (fi_multifile) && (finalize_strp (false), 0)))
+ {
+ cleanup ();
+ ret = 1;
+ }
+ else if (!(ignore_size || force_p)
+ && ((debug_sections[DEBUG_INFO].new_size
+ + debug_sections[DEBUG_ABBREV].new_size
+ + debug_sections[DEBUG_STR].new_size
+ + debug_sections[DEBUG_MACRO].new_size
+ + debug_sections[DEBUG_TYPES].new_size)
+ >= (debug_sections[DEBUG_INFO].size
+ + debug_sections[DEBUG_ABBREV].size
+ + debug_sections[DEBUG_STR].size
+ + debug_sections[DEBUG_MACRO].size
+ + debug_sections[DEBUG_TYPES].size)))
+ {
+ if (!quiet || outfile != NULL)
+ error (0, 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
+ + debug_sections[DEBUG_STR].size
+ + debug_sections[DEBUG_MACRO].size
+ + debug_sections[DEBUG_TYPES].size),
+ (unsigned long) (debug_sections[DEBUG_INFO].new_size
+ + debug_sections[DEBUG_ABBREV].new_size
+ + debug_sections[DEBUG_STR].new_size
+ + debug_sections[DEBUG_MACRO].new_size
+ + debug_sections[DEBUG_TYPES].new_size));
+
+ if (multifile && !fi_multifile && !low_mem)
+ write_multifile (dso, res);
+
+ cleanup ();
+ if (outfile != NULL)
+ ret = 1;
+ }
+ else if (write_aranges (dso))
+ {
+ failure:
+ cleanup ();
+ ret = 1;
+ }
+ else
+ {
+ if (unlikely (fi_multifile))
+ {
+ size_t len;
+ const char *name = multifile_name;
+ enum debug_section_kind sec_kind;
+ unsigned char *ptr;
+ if (multifile_name == NULL)
+ {
+ if (!multifile_relative)
+ name = multifile;
+ else
+ {
+ char *p1 = realpath (file, NULL);
+ char *p2 = realpath (multifile, NULL);
+ char *p3, *p4, *p5, *p6;
+ unsigned int dotdot = 0;
+ if (p1 == NULL || p2 == NULL)
+ {
+ if (p1)
+ free (p1);
+ else if (p2)
+ free (p2);
+ error (0, 0, "Could not compute relative multifile "
+ "pathname from %s to %s",
+ file, multifile);
+ goto failure;
+ }
+ p3 = p1;
+ p4 = p2;
+ do
+ {
+ p5 = strchr (p3, '/');
+ p6 = strchr (p4, '/');
+ if (p5 == NULL
+ || p6 == NULL
+ || p5 - p3 != p6 - p4
+ || memcmp (p3, p4, p5 - p3) != 0)
+ break;
+ p3 = p5 + 1;
+ p4 = p6 + 1;
+ }
+ while (1);
+ while (p5 != NULL)
+ {
+ dotdot++;
+ p5 = strchr (p5 + 1, '/');
+ }
+ len = strlen (p4);
+ p3 = (char *) malloc (dotdot * 3 + len + 1);
+ if (p3 == NULL)
+ dwz_oom ();
+ p5 = p3;
+ while (dotdot)
+ {
+ memcpy (p5, "../", 3);
+ p5 += 3;
+ dotdot--;
+ }
+ memcpy (p5, p4, len + 1);
+ free (p1);
+ free (p2);
+ name = p3;
+ }
+ }
+ len = strlen (name) + 1;
+ sec_kind = dwarf_5 ? DEBUG_SUP : GNU_DEBUGALTLINK;
+ debug_sections[sec_kind].new_size
+ = len + 0x14 + (dwarf_5 ? 4 : 0);
+ debug_sections[sec_kind].new_data
+ = malloc (debug_sections[sec_kind].new_size);
+ if (debug_sections[sec_kind].new_data == NULL)
+ dwz_oom ();
+ ptr = debug_sections[sec_kind].new_data;
+ if (dwarf_5)
+ {
+ write_16 (ptr, 5);
+ write_8 (ptr, 0);
+ }
+ memcpy (ptr, name, len);
+ ptr += len;
+ if (dwarf_5)
+ write_uleb128 (ptr, 0x14);
+ memcpy (ptr, multifile_sha1, 0x14);
+ if (name != multifile_name && name != multifile)
+ free ((void *) name);
+ write_macro ();
+ }
+ write_abbrev ();
+ write_info (die_count);
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_loc\n");
+ }
+ write_loc ();
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_loclists\n");
+ }
+ write_loclists ();
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_types\n");
+ }
+ write_types ();
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_gdb_index\n");
+ }
+ write_gdb_index ();
+ /* These sections are optional and it is unclear
+ how to adjust them. Just remove them. */
+ debug_sections[DEBUG_PUBNAMES].new_data = NULL;
+ debug_sections[DEBUG_PUBNAMES].new_size = 0;
+ debug_sections[DEBUG_PUBTYPES].new_data = NULL;
+ debug_sections[DEBUG_PUBTYPES].new_size = 0;
+ debug_sections[DEBUG_GNU_PUBNAMES].new_data = NULL;
+ debug_sections[DEBUG_GNU_PUBNAMES].new_size = 0;
+ debug_sections[DEBUG_GNU_PUBTYPES].new_data = NULL;
+ debug_sections[DEBUG_GNU_PUBTYPES].new_size = 0;
+
+ if (multifile && !fi_multifile && !low_mem)
+ write_multifile (dso, res);
+
+ bool save_to_temp = save_temps && multifile && multifile_mode == 0;
+ cleanup ();
+
+ if (write_dso (dso, outfile, &st, save_to_temp))
+ ret = 1;
+ else
+ res->res = 0;
+
+ if (unlikely (progress_p))
+ report_progress ();
+ }
+ }
+
+ for (i = 0; debug_sections[i].name; ++i)
+ {
+ debug_sections[i].data = NULL;
+ debug_sections[i].size = 0;
+ free (debug_sections[i].new_data);
+ debug_sections[i].new_data = NULL;
+ debug_sections[i].new_size = 0;
+ debug_sections[i].sec = 0;
+ }
+
+ if (elf_end (dso->elf) < 0)
+ {
+ error (0, 0, "elf_end failed: %s", elf_errmsg (elf_errno ()));
+ ret = 1;
+ }
+ close (fd);
+
+ free (dso);
+ if (ret == 3)
+ {
+ ret = (outfile != NULL) ? 1 : 0;
+ res->res = -1;
+ }
+ return ret;
+}
+
+/* In order to free all malloced memory at the end of optimize_multifile,
+ communicate .debug_str tail optimized offset list from optimize_multifile
+ to read_multifile using an mmapped chunk of memory pointed by this
+ variable. */
+static unsigned int *strp_tail_off_list;
+
+/* Process temporary .debug_* files, see what can be beneficially shared
+ and write a new ET_REL file, containing the shared .debug_* sections. */
+static int
+optimize_multifile (unsigned int *die_count)
+{
+ DSO dsobuf, *dso;
+ int fd = -1;
+ volatile int vfd = -1;
+ unsigned int i;
+ Elf *elf = NULL;
+ Elf *volatile velf = NULL;
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ char *e_ident;
+ static const char shstrtab_gnu[]
+ = "\0.shstrtab\0.note.gnu.build-id\0.gdb_index\0"
+ ".debug_info\0.debug_abbrev\0.debug_line\0.debug_str\0.debug_macro";
+ static const char shstrtab_dwarf5[]
+ = "\0.shstrtab\0.gdb_index\0"
+ ".debug_info\0.debug_abbrev\0.debug_line\0.debug_str\0.debug_macro\0"
+ ".debug_sup";
+ const char *const shstrtab = dwarf_5 ? shstrtab_dwarf5 : shstrtab_gnu;
+ const size_t shstrtab_len = (dwarf_5
+ ? sizeof shstrtab_dwarf5
+ : sizeof shstrtab_gnu);
+ const char *p;
+ unsigned char note[0x24], *np, *supp;
+ struct sha1_ctx ctx;
+
+ if (multi_ehdr.e_ident[0] == '\0'
+ || multi_ptr_size == 0
+ || multi_endian == 0)
+ return -1;
+
+ if (multi_line_off == 0)
+ {
+ init_endian (multi_endian);
+ if (write_multifile_line ())
+ {
+ error (0, 0, "Error writing multi-file temporary files");
+ return -1;
+ }
+ }
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "optimize_multifile\n");
+ }
+
+ debug_sections[DEBUG_INFO].size = multi_info_off;
+ debug_sections[DEBUG_INFO].data
+ = (multi_info_off
+ ? mmap (NULL, multi_info_off, PROT_READ, MAP_PRIVATE, multi_info_fd, 0)
+ : NULL);
+ debug_sections[DEBUG_ABBREV].size = multi_abbrev_off;
+ debug_sections[DEBUG_ABBREV].data
+ = (multi_abbrev_off
+ ? mmap (NULL, multi_abbrev_off, PROT_READ, MAP_PRIVATE,
+ multi_abbrev_fd, 0)
+ : NULL);
+ debug_sections[DEBUG_LINE].size = multi_line_off;
+ debug_sections[DEBUG_LINE].data
+ = mmap (NULL, multi_line_off, PROT_READ, MAP_PRIVATE, multi_line_fd, 0);
+ debug_sections[DEBUG_STR].size = multi_str_off;
+ debug_sections[DEBUG_STR].data
+ = multi_str_off
+ ? mmap (NULL, multi_str_off, PROT_READ, MAP_PRIVATE, multi_str_fd, 0)
+ : NULL;
+ debug_sections[DEBUG_MACRO].size = multi_macro_off;
+ debug_sections[DEBUG_MACRO].data
+ = multi_macro_off
+ ? mmap (NULL, multi_macro_off, PROT_READ, MAP_PRIVATE, multi_macro_fd, 0)
+ : NULL;
+ if (debug_sections[DEBUG_INFO].data == MAP_FAILED
+ || debug_sections[DEBUG_ABBREV].data == MAP_FAILED
+ || debug_sections[DEBUG_LINE].data == MAP_FAILED
+ || debug_sections[DEBUG_STR].data == MAP_FAILED
+ || debug_sections[DEBUG_MACRO].data == MAP_FAILED)
+ {
+ error (0, 0, "Error mmapping multi-file temporary files");
+ fail:
+ cleanup ();
+ if (velf)
+ elf_end (velf);
+ if (vfd != -1)
+ {
+ unlink (multifile);
+ close (vfd);
+ }
+ if (debug_sections[DEBUG_INFO].data != MAP_FAILED)
+ munmap (debug_sections[DEBUG_INFO].data,
+ debug_sections[DEBUG_INFO].size);
+ if (debug_sections[DEBUG_ABBREV].data != MAP_FAILED)
+ munmap (debug_sections[DEBUG_ABBREV].data,
+ debug_sections[DEBUG_ABBREV].size);
+ if (debug_sections[DEBUG_LINE].data != MAP_FAILED)
+ munmap (debug_sections[DEBUG_LINE].data,
+ debug_sections[DEBUG_LINE].size);
+ if (debug_sections[DEBUG_STR].data != MAP_FAILED
+ && debug_sections[DEBUG_STR].data != NULL)
+ munmap (debug_sections[DEBUG_STR].data,
+ debug_sections[DEBUG_STR].size);
+ if (debug_sections[DEBUG_MACRO].data != MAP_FAILED
+ && debug_sections[DEBUG_MACRO].data != NULL)
+ munmap (debug_sections[DEBUG_MACRO].data,
+ debug_sections[DEBUG_MACRO].size);
+ return -1;
+ }
+
+ init_endian (multi_endian);
+ ptr_size = multi_ptr_size;
+ memset (&dsobuf, '\0', sizeof (dsobuf));
+ dso = &dsobuf;
+ dso->filename = multifile;
+ if (tracing)
+ fprintf (stderr, "Optimize-multifile\n");
+ multifile_mode = MULTIFILE_MODE_OP;
+
+ obstack_alloc_failed_handler = dwz_oom;
+ if (unoptimized_multifile)
+ {
+ for (i = 0; i < SAVED_SECTIONS; i++)
+ {
+ debug_sections[i].new_data = debug_sections[i].data;
+ debug_sections[i].new_size = debug_sections[i].size;
+ }
+ }
+ else if (setjmp (oom_buf))
+ {
+ error (0, ENOMEM, "%s: Could not allocate memory", dso->filename);
+ goto fail;
+ }
+ else
+ {
+ dw_cu_ref *cup;
+ unsigned char *p, *q;
+ unsigned int strp_count;
+
+ obstack_init (&ob);
+ obstack_init (&ob2);
+
+ if (read_debug_info (dso, DEBUG_INFO, NULL)
+ || partition_dups ())
+ goto fail;
+
+#if DEVEL
+ clear_p2_field ();
+#endif
+
+ for (cup = &first_cu; *cup && (*cup)->cu_kind == CU_PU;
+ cup = &(*cup)->cu_next)
+ ;
+
+ *cup = NULL;
+
+ strp_count = debug_sections[DEBUG_STR].size / 64;
+ if (strp_count < 64)
+ strp_count = 64;
+ strp_htab = htab_try_create (strp_count,
+ strp_hash2, strp_eq2, NULL);
+ if (strp_htab == NULL)
+ dwz_oom ();
+
+ for (p = debug_sections[DEBUG_STR].data;
+ p < debug_sections[DEBUG_STR].data + debug_sections[DEBUG_STR].size;
+ p = q + 1)
+ {
+ void **slot;
+ struct strp_entry se;
+ hashval_t hash;
+
+ q = (unsigned char *) strchr ((char *) p, '\0');
+ hash = hash (p, q - p);
+ se.off = p - debug_sections[DEBUG_STR].data;
+ se.new_off = hash & ~1U;
+ slot = htab_find_slot_with_hash (strp_htab, &se, se.new_off, INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ if (*slot == NULL)
+ {
+ struct strp_entry *s = pool_alloc (strp_entry, sizeof (*s));
+ *s = se;
+ *slot = (void *) s;
+ }
+ else
+ ((struct strp_entry *) *slot)->new_off |= 1;
+ }
+
+ if (first_cu != NULL)
+ {
+ if (compute_abbrevs (dso))
+ goto fail;
+
+ strp_tail_off_list = finalize_strp (true);
+
+ write_abbrev ();
+ write_info (die_count);
+ write_gdb_index ();
+ if (write_multifile_line ())
+ goto fail;
+ }
+ else
+ strp_tail_off_list = finalize_strp (true);
+
+ if (debug_sections[DEBUG_MACRO].data)
+ handle_macro ();
+ }
+
+ np = note;
+ write_32 (np, sizeof ("GNU"));
+ write_32 (np, 0x14);
+ write_32 (np, NT_GNU_BUILD_ID);
+
+ supp = NULL;
+ if (dwarf_5)
+ {
+ debug_sections[DEBUG_SUP].new_size = 0x14 + 5;
+ debug_sections[DEBUG_SUP].new_data
+ = malloc (debug_sections[DEBUG_SUP].new_size);
+ if (debug_sections[DEBUG_SUP].new_data == NULL)
+ dwz_oom ();
+ supp = debug_sections[DEBUG_SUP].new_data;
+ write_16 (supp, 5);
+ write_8 (supp, 1);
+ write_8 (supp, 0);
+ write_uleb128 (supp, 0x14);
+ }
+
+ cleanup ();
+ fd = open (multifile, O_RDWR | O_CREAT, 0600);
+ vfd = fd;
+ if (fd < 0)
+ {
+ error (0, errno, "Failed to open multi-file common file %s", multifile);
+ goto fail;
+ }
+
+ elf = elf_begin (fd, ELF_C_WRITE, NULL);
+ velf = elf;
+ if (elf == NULL)
+ {
+ error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
+ goto fail;
+ }
+
+ multi_ehdr.e_type = ET_REL;
+ multi_ehdr.e_entry = 0;
+ multi_ehdr.e_phoff = 0;
+ multi_ehdr.e_phnum = 0;
+ multi_ehdr.e_shoff = multi_ehdr.e_ehsize;
+ multi_ehdr.e_shnum = 2;
+ if (!dwarf_5)
+ {
+ multi_ehdr.e_shoff += 0x24;
+ multi_ehdr.e_shnum++;
+ }
+ for (i = 0; debug_sections[i].name; i++)
+ if (debug_sections[i].new_size)
+ {
+ multi_ehdr.e_shoff += debug_sections[i].new_size;
+ multi_ehdr.e_shnum++;
+ }
+ multi_ehdr.e_shstrndx = multi_ehdr.e_shnum - 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 (multi_ehdr.e_ident[EI_CLASS])
+ {
+ case ELFCLASS32:
+ e_ident = (char *) elf32_newehdr (elf);
+ multi_ehdr.e_shoff = (multi_ehdr.e_shoff + 3) & -4;
+ break;
+ case ELFCLASS64:
+ e_ident = (char *) elf64_newehdr (elf);
+ multi_ehdr.e_shoff = (multi_ehdr.e_shoff + 7) & -8;
+ 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, multi_ehdr.e_ident, EI_NIDENT) == NULL
+ || gelf_update_ehdr (elf, &multi_ehdr) == 0)
+ {
+ error (0, 0, "Could not create new ELF headers");
+ goto fail;
+ }
+ elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE);
+
+ sha1_init_ctx (&ctx);
+ for (i = 0; debug_sections[i].name; i++)
+ {
+ if (debug_sections[i].new_size == 0)
+ continue;
+ sha1_process_bytes (debug_sections[i].new_data,
+ debug_sections[i].new_size, &ctx);
+ }
+ sha1_finish_ctx (&ctx, multifile_sha1);
+
+ memcpy (np, "GNU", sizeof ("GNU"));
+ memcpy (np + 4, multifile_sha1, 0x14);
+
+ if (dwarf_5)
+ memcpy (supp, multifile_sha1, 0x14);
+
+ memset (&shdr, '\0', sizeof (shdr));
+ shdr.sh_offset = multi_ehdr.e_ehsize;
+ if (!dwarf_5)
+ {
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_addralign = 4;
+ shdr.sh_size = 0x24;
+ scn = elf_newscn (elf);
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ shdr.sh_name = (strchr (shstrtab + 1, '\0') + 1) - shstrtab;
+ gelf_update_shdr (scn, &shdr);
+ data = elf_newdata (scn);
+ data->d_buf = (char *) note;
+ data->d_type = ELF_T_BYTE;
+ data->d_version = EV_CURRENT;
+ data->d_size = shdr.sh_size;
+ data->d_off = 0;
+ data->d_align = 1;
+ }
+
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_offset += shdr.sh_size;
+ shdr.sh_addralign = 1;
+ for (i = 0; debug_sections[i].name; i++)
+ {
+ if (debug_sections[i].new_size == 0)
+ continue;
+ scn = elf_newscn (elf);
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ for (p = shstrtab + 1; p < shstrtab + shstrtab_len;
+ p = strchr (p, '\0') + 1)
+ if (strcmp (p, debug_sections[i].name) == 0)
+ {
+ shdr.sh_name = p - shstrtab;
+ break;
+ }
+ shdr.sh_size = debug_sections[i].new_size;
+ if (i == DEBUG_STR)
+ {
+ shdr.sh_flags = SHF_MERGE | SHF_STRINGS;
+ shdr.sh_entsize = 1;
+ }
+ else
+ {
+ shdr.sh_flags = 0;
+ shdr.sh_entsize = 0;
+ }
+ gelf_update_shdr (scn, &shdr);
+ data = elf_newdata (scn);
+ data->d_buf = debug_sections[i].new_data;
+ data->d_type = ELF_T_BYTE;
+ data->d_version = EV_CURRENT;
+ data->d_size = shdr.sh_size;
+ data->d_off = 0;
+ data->d_align = 1;
+ shdr.sh_offset += shdr.sh_size;
+ }
+ scn = elf_newscn (elf);
+ elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY);
+ shdr.sh_name = 1;
+ shdr.sh_offset = multi_ehdr.e_shoff
+ + multi_ehdr.e_shnum * multi_ehdr.e_shentsize;
+ shdr.sh_size = shstrtab_len;
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_flags = 0;
+ shdr.sh_entsize = 0;
+ gelf_update_shdr (scn, &shdr);
+ data = elf_newdata (scn);
+ data->d_buf = (char *) shstrtab;
+ data->d_type = ELF_T_BYTE;
+ data->d_version = EV_CURRENT;
+ data->d_size = shdr.sh_size;
+ data->d_off = 0;
+ data->d_align = 1;
+
+ if (elf_update (elf, ELF_C_WRITE_MMAP) == -1)
+ {
+ error (0, 0, "%s: elf_update failed", multifile);
+ goto fail;
+ }
+
+ if (elf_end (elf) < 0)
+ {
+ error (0, 0, "elf_end failed: %s", elf_errmsg (elf_errno ()));
+ goto fail;
+ }
+
+ fchmod (fd, 0644);
+
+ if (dwarf_5)
+ {
+ free (debug_sections[DEBUG_SUP].new_data);
+ debug_sections[DEBUG_SUP].new_data = NULL;
+ }
+ munmap (debug_sections[DEBUG_INFO].data, debug_sections[DEBUG_INFO].size);
+ munmap (debug_sections[DEBUG_ABBREV].data,
+ debug_sections[DEBUG_ABBREV].size);
+ munmap (debug_sections[DEBUG_LINE].data, debug_sections[DEBUG_LINE].size);
+ if (debug_sections[DEBUG_STR].data)
+ munmap (debug_sections[DEBUG_STR].data, debug_sections[DEBUG_STR].size);
+ if (debug_sections[DEBUG_MACRO].data)
+ munmap (debug_sections[DEBUG_MACRO].data,
+ debug_sections[DEBUG_MACRO].size);
+
+ for (i = 0; debug_sections[i].name; ++i)
+ {
+ debug_sections[i].data = NULL;
+ debug_sections[i].size = 0;
+ if (!unoptimized_multifile)
+ free (debug_sections[i].new_data);
+ debug_sections[i].new_data = NULL;
+ debug_sections[i].new_size = 0;
+ debug_sections[i].sec = 0;
+ }
+
+ return fd;
+}
+
+/* Parse the .debug_* sections from shared ET_REL file written
+ by optimize_multifile into data structures for fi_multifile
+ phase. */
+static DSO *
+read_multifile (int fd, unsigned int die_count)
+{
+ DSO *dso, *volatile ret;
+ unsigned int i;
+
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "read_multifile\n");
+ }
+
+ if (tracing)
+ fprintf (stderr, "Read-multifile\n");
+
+ multifile_mode = MULTIFILE_MODE_RD;
+ dso = fdopen_dso (fd, multifile);
+ if (dso == NULL)
+ {
+ multifile_mode = 0;
+ return NULL;
+ }
+
+ ret = dso;
+ obstack_alloc_failed_handler = dwz_oom;
+ if (setjmp (oom_buf))
+ {
+ error (0, ENOMEM, "%s: Could not allocate memory", dso->filename);
+
+ fail:
+ elf_end (dso->elf);
+ close (fd);
+ free (dso);
+ ret = NULL;
+ alt_off_htab = NULL;
+ }
+ else
+ {
+ obstack_init (&ob);
+ obstack_init (&ob2);
+
+ if (read_dwarf (dso, false, &die_count))
+ goto fail;
+
+ if (debug_sections[DEBUG_STR].size)
+ {
+ unsigned char *p, *q;
+ unsigned int strp_count = debug_sections[DEBUG_STR].size / 64;
+ void **slot;
+ unsigned int *pi;
+
+ if (strp_count < 100)
+ strp_count = 100;
+ strp_htab = htab_try_create (strp_count, strp_hash3, strp_eq3, NULL);
+ if (strp_htab == NULL)
+ dwz_oom ();
+ for (p = debug_sections[DEBUG_STR].data;
+ p < debug_sections[DEBUG_STR].data
+ + debug_sections[DEBUG_STR].size; p = q)
+ {
+ q = (unsigned char *) strchr ((char *) p, '\0') + 1;
+ slot = htab_find_slot_with_hash (strp_htab, p,
+ hash (p, q - p - 1), INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ assert (*slot == NULL);
+ *slot = (void *) p;
+ }
+ if (strp_tail_off_list)
+ {
+ for (pi = strp_tail_off_list; *pi; pi++)
+ {
+ p = debug_sections[DEBUG_STR].data + *pi;
+ q = (unsigned char *) strchr ((char *) p, '\0');
+ slot = htab_find_slot_with_hash (strp_htab, p,
+ hash (p, q - p), INSERT);
+ if (slot == NULL)
+ dwz_oom ();
+ assert (*slot == NULL);
+ *slot = (void *) p;
+ }
+ pi++;
+ munmap (strp_tail_off_list,
+ (char *) pi - (char *) strp_tail_off_list);
+ }
+ }
+
+ if (debug_sections[DEBUG_MACRO].data)
+ handle_macro ();
+
+ alt_strp_htab = strp_htab;
+ strp_htab = NULL;
+ alt_off_htab = off_htab;
+ off_htab = NULL;
+ alt_dup_htab = dup_htab;
+ dup_htab = NULL;
+ alt_macro_htab = macro_htab;
+ macro_htab = NULL;
+ alt_first_cu = first_cu;
+ alt_pool = finalize_pool ();
+ alt_ob = ob;
+ alt_ob2 = ob2;
+ memset (&ob, '\0', sizeof (ob));
+ memset (&ob2, '\0', sizeof (ob2));
+ for (i = 0; i < SAVED_SECTIONS; i++)
+ {
+ alt_data[i] = debug_sections[i].data;
+ alt_size[i] = debug_sections[i].size;
+ }
+ }
+
+ cleanup ();
+
+ for (i = 0; debug_sections[i].name; ++i)
+ {
+ debug_sections[i].data = NULL;
+ debug_sections[i].size = 0;
+ debug_sections[i].new_data = NULL;
+ debug_sections[i].new_size = 0;
+ debug_sections[i].sec = 0;
+ }
+
+ return ret;
+}
+
+/* Clear all die_nextdup fields among toplevel children
+ of DIE. */
+static void
+alt_clear_dups (dw_die_ref die)
+{
+ dw_die_ref child;
+ assert (die->die_dup == NULL);
+ die->die_nextdup = NULL;
+ for (child = die->die_child; child; child = child->die_sib)
+ {
+ assert (child->die_dup == NULL);
+ child->die_nextdup = NULL;
+ if (child->die_named_namespace)
+ alt_clear_dups (child);
+ }
+}
+
+/* Create a temporary file using NAME. Return the corresponding file
+ descriptor if successful, otherwise return -1. */
+static int
+make_temp_file (const char *name)
+{
+ const char *tmpdir = "/tmp/";
+ const char *template_suffix = ".XXXXXX";
+ int fd;
+ size_t buf_len, offset, name_len;
+ char *buf;
+
+ if (save_temps)
+ {
+ FILE *f = fopen (name, "w+");
+ if (f == NULL)
+ fd = -1;
+ else
+ fd = fileno (f);
+ return fd;
+ }
+
+ name_len = strlen (name);
+ buf_len = (strlen (tmpdir)
+ + name_len
+ + strlen (template_suffix)
+ + 1);
+ if (buf_len < name_len)
+ return -1;
+ buf = (char *)malloc (buf_len);
+ if (buf == NULL)
+ return -1;
+ offset = 0;
+
+ strcpy (&buf[offset], tmpdir);
+ offset += strlen (tmpdir);
+
+ strcpy (&buf[offset], name);
+ offset += name_len;
+
+ strcpy (&buf[offset], template_suffix);
+ offset += strlen (template_suffix);
+
+ assert (offset == buf_len - 1);
+ assert (buf[offset] == '\0');
+
+ fd = mkstemp (buf);
+ if (fd == -1)
+ goto done;
+
+ /* Unlink the filename, such that the file is disposed of once the file
+ descriptor is closed. */
+ unlink (buf);
+
+ done:
+ free (buf);
+ return fd;
+}
+
+/* As dwz, but retry with MULTIFILE_MODE_LOW_MEM if the low_mem_die_limit
+ is hit. */
+static int
+dwz_with_low_mem (const char *file, const char *outfile,
+ struct file_result *res)
+{
+ int ret;
+
+ res->low_mem_p = false;
+
+ ret = (low_mem_die_limit == 0
+ ? 2
+ : dwz (file, outfile, res));
+
+ if (ret == 2)
+ {
+ multifile_mode = MULTIFILE_MODE_LOW_MEM;
+ res->low_mem_p = true;
+
+ ret = dwz (file, outfile, res);
+ }
+
+ return ret;
+}
+
+/* Initialize struct file_result RES. */
+static void
+init_file_result (struct file_result *res)
+{
+ res->die_count = 0;
+ res->res = -3;
+ res->skip_multifile = false;
+ res->low_mem_p = false;
+}
+
+/* Dwarf-compress FILE. If OUTFILE, write to result to OUTFILE, otherwise
+ modify FILE. */
+static int
+dwz_one_file (const char *file, const char *outfile)
+{
+ struct file_result res;
+
+ if (stats_p)
+ init_stats (file);
+
+ init_file_result (&res);
+
+ return dwz_with_low_mem (file, outfile, &res);
+}
+
+/* Helper structure for hardlink discovery. */
+struct hl_stat
+{
+ dev_t dev;
+ ino_t ino;
+ nlink_t nlink;
+};
+
+/* Detect which FILES are hardlinks, and mark those in RESA. */
+static bool
+detect_hardlinks (int nr_files, char *files[], struct file_result *resa)
+{
+ bool found = false;
+ struct hl_stat hl_stat[nr_files];
+ int i;
+
+ /* Try to open all files. */
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ int fd;
+ struct stat st;
+
+ const char *file = files[i];
+ res->res = -1;
+
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ error (0, errno, "Failed to open input file %s", file);
+ else if (fstat (fd, &st) < 0)
+ error (0, errno, "Failed to stat input file %s", file);
+ else
+ {
+ res->res = 1;
+ hl_stat[i].dev = st.st_dev;
+ hl_stat[i].ino = st.st_ino;
+ hl_stat[i].nlink = st.st_nlink;
+ }
+
+ close (fd);
+ }
+
+ /* Detect hard links. */
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ int n;
+ for (n = 0; n != i; n++)
+ if (resa[n].res >= 0
+ && hl_stat[n].nlink > 1
+ && hl_stat[n].dev == hl_stat[i].dev
+ && hl_stat[n].ino == hl_stat[i].ino)
+ break;
+ if (n == i)
+ continue;
+ res->res = -2;
+ res->hardlink_to = n;
+ found = true;
+ }
+
+ return found;
+}
+
+/* Update the FILES marked as hardlink in RESA. */
+static void
+update_hardlinks (int nr_files, char *files[], struct file_result *resa)
+{
+ int i;
+
+ /* Update hardlinks. */
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ const char *file = files[i];
+ size_t n;
+ if (res->res != -2)
+ continue;
+ n = res->hardlink_to;
+
+ /* If a hardlink to this has been processed before
+ and we didn't change it, just assume the same
+ state. */
+ if (resa[n].res == 1)
+ {
+ if (tracing)
+ fprintf (stderr, "Skipping hardlink %s to unchanged file\n",
+ file);
+ continue;
+ }
+
+ /* If it changed, try to hardlink it again. */
+ if (resa[n].res == 0)
+ {
+ size_t len = strlen (file);
+ char *filename = alloca (len + sizeof (".#dwz#.XXXXXX"));
+ int fd2;
+ if (tracing)
+ fprintf (stderr, "Updating hardlink %s to changed file\n",
+ file);
+ memcpy (filename, file, len);
+ memcpy (filename + len, ".#dwz#.XXXXXX",
+ sizeof (".#dwz#.XXXXXX"));
+ fd2 = mkstemp (filename);
+ if (fd2 >= 0)
+ {
+ close (fd2);
+ unlink (filename);
+ if (link (files[n], filename) == 0)
+ {
+ if (rename (filename, file) == 0)
+ ;
+ else
+ unlink (filename);
+ }
+ }
+ }
+ }
+}
+
+/* Encode child process exit status. */
+static int
+encode_child_exit_status (int thisret, struct file_result *res)
+{
+ assert (thisret == 0 || thisret == 1);
+ if (thisret == 0 && res->low_mem_p)
+ thisret = 2;
+ assert (res->res >= -3 && res->res <= 1);
+ return (thisret
+ + ((res->res + 3) << 2)
+ + ((res->skip_multifile ? 1 : 0) << 5));
+}
+
+/* Decode child process exit status. */
+static int
+decode_child_exit_status (int state, struct file_result *res)
+{
+ int ret;
+ if (!WIFEXITED (state))
+ error (1, 0, "Child dwz process got killed");
+ int status = WEXITSTATUS (state);
+ ret = status & 0x3;
+ status >>= 2;
+
+ res->low_mem_p = false;
+ if (ret == 2)
+ {
+ ret = 0;
+ res->low_mem_p = true;
+ }
+
+ res->res = (int)(status & 0x7) - 3;
+ status >>= 3;
+
+ res->skip_multifile = (status & 0x1) ? true : false;
+
+ return ret;
+}
+
+/* Wait on child exit with PID, update PIDS and RES. */
+static void
+wait_child_exit (pid_t pid, pid_t *pids, int nr_pids,
+ struct file_result *resa)
+{
+ int state;
+ pid_t got_pid = waitpid (pid, &state, 0);
+
+ int i;
+ for (i = 0; i < nr_pids; ++i)
+ if (pids[i] == got_pid)
+ {
+ pids[i] = 0;
+ break;
+ }
+ assert (i < nr_pids);
+
+ resa[i].ret = decode_child_exit_status (state, &resa[i]);
+}
+
+static int *workset;
+static int workset_size = 0;
+int current_multifile_owner = -1;
+int current_multifile_owner_file_idx = -1;
+
+/* Wait on exit of chilren in PIDS, update RESA. */
+static void
+wait_children_exit (pid_t *pids, int nr_files, struct file_result *resa)
+{
+ int i;
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ if (pids[i] == 0)
+ continue;
+ wait_child_exit (pids[i], &pids[i], 1, res);
+ if (current_multifile_owner_file_idx == -1
+ || i < current_multifile_owner_file_idx)
+ continue;
+ assert (i == current_multifile_owner_file_idx);
+ current_multifile_owner++;
+ if (current_multifile_owner == workset_size)
+ continue;
+ current_multifile_owner_file_idx
+ = workset[current_multifile_owner];
+ pass_token (current_multifile_owner);
+ }
+}
+
+/* Dwarf-compress FILES. If HARDLINK, detect if some files are hardlinks and
+ if so, dwarf-compress just one, and relink the others. */
+static int
+dwz_files_1 (int nr_files, char *files[], bool hardlink,
+ struct file_result *resa)
+{
+ int ret = 0;
+ int i, j;
+ const char *file;
+ int successcount = 0;
+
+ for (i = 0; i < nr_files; ++i)
+ init_file_result (&resa[i]);
+
+ if (multifile)
+ {
+ if (multifile_force_ptr_size)
+ multi_ptr_size = multifile_force_ptr_size;
+ if (multifile_force_endian)
+ multi_endian = multifile_force_endian;
+
+ multi_info_fd = make_temp_file ("dwz.debug_info");
+ multi_abbrev_fd = make_temp_file ("dwz.debug_abbrev");
+ multi_line_fd = make_temp_file ("dwz.debug_line");
+ multi_str_fd = make_temp_file ("dwz.debug_str");
+ multi_macro_fd = make_temp_file ("dwz.debug_macro");
+ if (multi_info_fd == -1
+ || multi_abbrev_fd == -1
+ || multi_line_fd == -1
+ || multi_str_fd == -1
+ || multi_macro_fd == -1)
+ {
+ error (0, 0, "Could not create multifile temporary files");
+ multifile = NULL;
+ }
+ }
+
+ if (hardlink)
+ hardlink = detect_hardlinks (nr_files, files, resa);
+
+ workset = malloc (nr_files * sizeof (int));
+ if (workset == NULL)
+ error (1, ENOMEM, "failed to allocate workset array");
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ if (res->res == -2)
+ /* Skip hard links. */
+ continue;
+ workset[workset_size] = i;
+ workset_size++;
+ }
+
+ bool initial_parallel_p = max_forks > 1;
+ if (initial_parallel_p && multifile)
+ {
+ if (multifile_force_ptr_size != 0 && multifile_force_endian != 0)
+ {
+ write_multifile_parallel_p = true;
+ pipes = malloc (workset_size * 2 * sizeof (int));
+ if (pipes == NULL)
+ error (1, ENOMEM, "failed to allocate pipes array");
+ for (i = 0; i < workset_size; i++)
+ {
+ int fds[2];
+ if (pipe (fds) != 0)
+ error (1, ENOMEM, "failed to initialize pipe");
+ pipes[i].readfd = fds[0];
+ pipes[i].writefd = fds[1];
+ }
+ }
+ else
+ initial_parallel_p = false;
+ }
+ if (initial_parallel_p)
+ {
+ pid_t pids[nr_files];
+ int nr_forks = 0;
+ for (i = 0; i < nr_files; i++)
+ pids[i] = 0;
+ for (j = 0; j < workset_size; j++)
+ {
+ int i = workset[j];
+
+ if (nr_forks == max_forks)
+ {
+ if (multifile == NULL)
+ wait_child_exit (-1, pids, i, resa);
+ else
+ {
+ int k = current_multifile_owner_file_idx;
+ wait_child_exit (pids[k], &pids[k], 1, &resa[k]);
+ current_multifile_owner++;
+ current_multifile_owner_file_idx
+ = workset[current_multifile_owner];
+ pass_token (current_multifile_owner);
+ }
+ nr_forks--;
+ }
+
+ pid_t fork_res = fork ();
+ assert (fork_res != -1);
+ if (fork_res == 0)
+ {
+ child_id = j;
+ file = files[i];
+ struct file_result *res = &resa[i];
+ int thisret = dwz_with_low_mem (file, NULL, res);
+ return encode_child_exit_status (thisret, res);
+ }
+ else
+ {
+ if (multifile && j == 0)
+ {
+ current_multifile_owner = j;
+ current_multifile_owner_file_idx
+ = workset[current_multifile_owner];
+ pass_token (current_multifile_owner);
+ }
+ pids[i] = fork_res;
+ nr_forks++;
+ }
+ }
+ if (nr_forks > 0)
+ wait_children_exit (pids, nr_files, resa);
+ }
+ else
+ {
+ for (j = 0; j < workset_size; j++)
+ {
+ int i = workset[j];
+ file = files[i];
+ struct file_result *res = &resa[i];
+ if (stats_p)
+ init_stats (file);
+ res->ret = dwz_with_low_mem (file, NULL, res);
+ }
+ }
+
+ for (j = 0; j < workset_size; j++)
+ {
+ int i = workset[j];
+ struct file_result *res = &resa[i];
+ int thisret = res->ret;
+ if (thisret == 1)
+ ret = 1;
+ else if (!res->low_mem_p && !res->skip_multifile && res->res >= 0)
+ successcount++;
+ }
+
+ if (hardlink)
+ update_hardlinks (nr_files, files, resa);
+
+ if (multifile == NULL)
+ return ret;
+
+ if (successcount < 2)
+ {
+ error (0, 0, "Too few files for multifile optimization");
+ return ret;
+ }
+
+ if (write_multifile_parallel_p)
+ {
+ multi_info_off = lseek (multi_info_fd, 0L, SEEK_END);
+ multi_abbrev_off = lseek (multi_abbrev_fd, 0L, SEEK_END);
+ multi_line_off = lseek (multi_line_fd, 0L, SEEK_END);
+ multi_str_off = lseek (multi_str_fd, 0L, SEEK_END);
+ multi_macro_off = lseek (multi_macro_fd, 0L, SEEK_END);
+ }
+ if (multi_info_off == 0 && multi_str_off == 0 && multi_macro_off == 0)
+ {
+ if (!quiet)
+ error (0, 0, "No suitable DWARF found for multifile optimization");
+ return ret;
+ }
+
+ if (write_multifile_parallel_p)
+ {
+ /* We reproduce here what happens when we run sequentially. This is a
+ kludge that probably needs to be replaced by IPC. */
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ if (!res->low_mem_p && !res->skip_multifile && res->res >= 0)
+ {
+ int fd = open (files[i], O_RDONLY);
+ if (fd < 0)
+ return ret;
+ DSO *dso = fdopen_dso (fd, files[i]);
+ if (dso == NULL)
+ {
+ close (fd);
+ return ret;
+ }
+ assert (multi_ehdr.e_ident[0] == '\0');
+ multi_ehdr = dso->ehdr;
+ break;
+ }
+ }
+ }
+
+ unsigned int multifile_die_count = 0;
+ int multi_fd = optimize_multifile (&multifile_die_count);
+ DSO *dso;
+ if (multi_fd == -1)
+ return 1;
+
+ dso = read_multifile (multi_fd, multifile_die_count);
+ if (dso == NULL)
+ {
+ ret = 1;
+ goto cleanup;
+ }
+
+ workset_size = 0;
+ for (i = 0; i < nr_files; i++)
+ {
+ struct file_result *res = &resa[i];
+ /* Don't process again files that couldn't
+ be processed successfully. Also skip hard links. */
+ if (res->res == -1 || res->res == -2
+ || res->skip_multifile)
+ continue;
+ workset[workset_size] = i;
+ workset_size++;
+ }
+
+ bool finalize_multifile_parallel_p = max_forks > 1;
+ if (finalize_multifile_parallel_p)
+ {
+ pid_t pids[nr_files];
+ int nr_forks = 0;
+ for (i = 0; i < nr_files; i++)
+ pids[i] = 0;
+ for (j = 0; j < workset_size; j++)
+ {
+ int i = workset[j];
+
+ if (nr_forks == max_forks)
+ {
+ wait_child_exit (-1, pids, i, resa);
+ nr_forks--;
+ }
+
+ pid_t fork_res = fork ();
+ assert (fork_res != -1);
+ if (fork_res == 0)
+ {
+ file = files[i];
+ struct file_result *res = &resa[i];
+ multifile_mode = MULTIFILE_MODE_FI;
+ int thisret = dwz (file, NULL, res);
+ return encode_child_exit_status (thisret, res);
+ }
+ else
+ {
+ pids[i] = fork_res;
+ nr_forks++;
+ }
+ }
+ if (nr_forks > 0)
+ wait_children_exit (pids, nr_files, resa);
+ }
+ else
+ {
+ for (j = 0; j < workset_size; j++)
+ {
+ int i = workset[j];
+ struct file_result *res = &resa[i];
+ dw_cu_ref cu;
+ file = files[i];
+ if (stats_p)
+ init_stats (file);
+ multifile_mode = MULTIFILE_MODE_FI;
+ for (cu = alt_first_cu; cu; cu = cu->cu_next)
+ alt_clear_dups (cu->cu_die);
+ res->ret = dwz (file, NULL, res);
+ }
+ }
+
+ for (j = 0; j < workset_size; j++)
+ {
+ int i = workset[j];
+ struct file_result *res = &resa[i];
+ int thisret = res->ret;
+ ret |= thisret;
+ }
+
+ if (hardlink)
+ update_hardlinks (nr_files, files, resa);
+
+ elf_end (dso->elf);
+ close (multi_fd);
+ free (dso);
+
+ cleanup:
+ cleanup ();
+
+ strp_htab = alt_strp_htab;
+ off_htab = alt_off_htab;
+ dup_htab = alt_dup_htab;
+ macro_htab = alt_macro_htab;
+ ob = alt_ob;
+ ob2 = alt_ob2;
+ cleanup ();
+ pool_destroy (alt_pool);
+
+ return ret;
+}
+
+/* Wrapper around dwz_files_1 that takes care of malloc and free of resa. */
+static int
+dwz_files (int nr_files, char *files[], bool hardlink)
+{
+ int ret;
+ struct file_result *resa
+ = (struct file_result *) malloc ((nr_files) * sizeof (*resa));
+ if (resa == NULL)
+ error (1, ENOMEM, "failed to allocate result array");
+
+ ret = dwz_files_1 (nr_files, files, hardlink, resa);
+
+ free (resa);
+ return ret;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int ret;
+ const char *outfile;
+ bool hardlink;
+ int nr_files;
+ char **files;
+
+ state = XXH64_createState ();
+
+ if (elf_version (EV_CURRENT) == EV_NONE)
+ error (1, 0, "library out of date");
+
+ outfile = NULL;
+ hardlink = false;
+ parse_args (argc, argv, &hardlink, &outfile);
+ nr_files = argc - optind;
+ files = &argv[optind];
+
+ if (nr_files <= 1)
+ {
+ const char *file = nr_files == 0 ? "a.out" : files[0];
+
+ if (multifile != NULL)
+ {
+ error (0, 0, "Too few files for multifile optimization");
+ multifile = NULL;
+ }
+
+ ret = dwz_one_file (file, outfile);
+ }
+ else
+ {
+ if (outfile != NULL)
+ error (1, 0, "-o option not allowed for multiple files");
+
+ ret = dwz_files (nr_files, files, hardlink);
+ }
+
+ if (stats_p)
+ free (stats);
+
+ return ret;
+}