diff --git a/.gitignore b/.gitignore index bc78b47..d2d0a8a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ dwz dwz.sum dwz.log +native.c testsuite/dwz.tests/execs diff --git a/Makefile b/Makefile index edd00d8..8b7cf76 100644 --- a/Makefile +++ b/Makefile @@ -3,25 +3,60 @@ VPATH = $(srcdir) else srcdir=$(shell pwd) endif + CFLAGS = -O2 -g DWZ_VERSION := $(shell cat $(srcdir)/VERSION) -override CFLAGS += -Wall -W -D_FILE_OFFSET_BITS=64 \ - -DDWZ_VERSION='"$(DWZ_VERSION)"' $(shell cat $(srcdir)/COPYRIGHT_YEARS) +CFLAGS_VERSION = -DDWZ_VERSION='"$(DWZ_VERSION)"' +CFLAGS_COPYRIGHT = $(shell cat $(srcdir)/COPYRIGHT_YEARS) +CFLAGS_COMMON = -Wall -W -D_FILE_OFFSET_BITS=64 +XXH_PROG = "\#define XXH_INLINE_ALL 1\n\#include \n" +XXH_INLINE_ALL_WORKS = $(shell printf $(XXH_PROG) \ + | $(CC) -xc -c - -o /dev/null 2>/dev/null \ + && echo -n 1) +ifeq "$(XXH_INLINE_ALL_WORKS)" "1" + CFLAGS_COMMON += -DXXH_INLINE_ALL=1 +endif + +override CFLAGS += $(CFLAGS_COMMON) $(CFLAGS_VERSION) $(CFLAGS_COPYRIGHT) + prefix = /usr exec_prefix = $(prefix) bindir = $(exec_prefix)/bin datarootdir = $(prefix)/share mandir = $(datarootdir)/man -OBJECTS = dwz.o hashtab.o sha1.o dwarfnames.o +OBJECTS = args.o dwz.o hashtab.o pool.o sha1.o dwarfnames.o +LIBS=-lelf +ifneq "$(XXH_INLINE_ALL_WORKS)" "1" +LIBS += -lxxhash +endif dwz: $(OBJECTS) - $(CC) $(LDFLAGS) -o $@ $^ -lelf + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) +args.o: native.o +args.o: CFLAGS_FOR_SOURCE = \ + -DNATIVE_ENDIAN_VAL=$(NATIVE_ENDIAN_VAL) \ + -DNATIVE_POINTER_SIZE=$(NATIVE_POINTER_SIZE) +NATIVE_ENDIAN=$(shell readelf -h native.o \ + | grep Data \ + | sed 's/.*, //;s/ endian//') +NATIVE_ENDIAN_LITTLE=$(findstring $(NATIVE_ENDIAN),$(findstring little,$(NATIVE_ENDIAN))) +NATIVE_ENDIAN_BIG=$(findstring $(NATIVE_ENDIAN),$(findstring big,$(NATIVE_ENDIAN))) +NATIVE_ENDIAN_VAL=$(if $(NATIVE_ENDIAN_LITTLE),ELFDATA2LSB,$(if $(NATIVE_ENDIAN_BIG),ELFDATA2MSB,ELFDATANONE)) +NATIVE_POINTER_SIZE=$(shell readelf -wi native.o \ + | grep "Pointer Size:" \ + | sed 's/.*: *//') +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< $(CFLAGS_FOR_SOURCE) install: dwz install -D dwz $(DESTDIR)$(bindir)/dwz install -D -m 644 $(srcdir)/dwz.1 $(DESTDIR)$(mandir)/man1/dwz.1 clean: - rm -f $(OBJECTS) *~ core* dwz $(TEST_EXECS) $(DWZ_TEST_SOURCES) \ - dwz.log dwz.sum + rm -f $(OBJECTS) *~ core* dwz $(TEST_EXECS) $(DWZ_TEST_OBJECTS) \ + dwz.log dwz.sum native.c native.o rm -Rf testsuite-bin tmp.* +native.c: + echo "int main (void) { return 0; }" > $@ +native.o: native.c + $(CC) -o $@ $< -c -g PWD:=$(shell pwd -P) @@ -33,7 +68,7 @@ TEST_EXECS_x86_64 = py-section-script dw2-skip-prologue \ TEST_EXECS = hello dwz-for-test min two-typedef start hello-gold-gdb-index \ start-gold hello-gnu-pubnames $(TEST_EXECS_DWARF_ASM) \ $(TEST_EXECS_$(UNAME)) odr-struct odr-class odr-union odr-struct-ns \ - odr-class-ns odr-union-ns odr-loc def-decl + odr-class-ns odr-union-ns odr-loc def-decl cycle UNAME:=$(shell uname -p) @@ -50,15 +85,21 @@ dw2-skip-prologue: py-section-script: $(CC) $(TEST_SRC)/py-section-script.s -o $@ -g || touch $@ -DWZ_TEST_SOURCES := $(patsubst %.o,%-for-test.c,$(OBJECTS)) - -%-for-test.c: %.c - sed 's/__GNUC__/NOT_DEFINED/' $< > $@ - -dwz-for-test: $(DWZ_TEST_SOURCES) - $(CC) $(DWZ_TEST_SOURCES) -O2 -g -lelf -o $@ -Wall -W -DDEVEL \ - -D_FILE_OFFSET_BITS=64 -DDWZ_VERSION='"for-test"' -I$(srcdir) \ - $(shell cat $(srcdir)/COPYRIGHT_YEARS) +DWZ_TEST_OBJECTS := $(patsubst %.o,%-for-test.o,$(OBJECTS)) +dwz-for-test: $(DWZ_TEST_OBJECTS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + rm -f $(DWZ_TEST_OBJECTS) +args-for-test.o: CFLAGS_FOR_SOURCE = \ + -DNATIVE_ENDIAN_VAL=$(NATIVE_ENDIAN_VAL) \ + -DNATIVE_POINTER_SIZE=$(NATIVE_POINTER_SIZE) +$(DWZ_TEST_OBJECTS): %-for-test.o : %.c + $(CC) $< -o $@ -c \ + -DUSE_GNUC=0 -DDEVEL \ + -O2 -g \ + $(CFLAGS_COMMON) \ + -DDWZ_VERSION='"for-test"' \ + $(CFLAGS_COPYRIGHT) \ + $(CFLAGS_FOR_SOURCE) min: $(CC) $(TEST_SRC)/min.c $(TEST_SRC)/min-2.c -o $@ -g @@ -96,11 +137,11 @@ $(TEMP_ASM_FILES): %-dw.S: $(TEST_SRC)/../lib/%.exp export DEJAGNU=$(DEJAGNU); \ runtest --tool=dwz -srcdir $(srcdir)/testsuite/ lib/$*.exp -$(filter-out no-multifile-prop, $(TEST_EXECS_DWARF_ASM)): %: %-dw.S +$(filter-out no-multifile-prop unavailable-dwarf-piece, $(TEST_EXECS_DWARF_ASM)): %: %-dw.S $(CC) $(TEST_SRC)/main.c $< -o $@ # Fails to compile on riscv64: Error: non-constant .uleb128 is not supported. -no-multifile-prop: %: %-dw.S +no-multifile-prop unavailable-dwarf-piece: %: %-dw.S $(CC) $(TEST_SRC)/main.c $< -o $@ || true odr-struct: @@ -136,14 +177,25 @@ def-decl: $(CXX) $(TEST_SRC)/decl.cc $(TEST_SRC)/def.cc $(TEST_SRC)/def2.cc \ -I$(TEST_SRC) -o $@ -g +cycle: + $(CC) $(TEST_SRC)/cycle.c -o $@ -g + # On some systems we need to set and export DEJAGNU to suppress # WARNING: Couldn't find the global config file. DEJAGNU ?= /dev/null -check: dwz $(TEST_EXECS) +VALGRIND_OPTIONS = -q --error-exitcode=99 + +check check-valgrind: dwz $(TEST_EXECS) mkdir -p testsuite-bin - cd testsuite-bin; ln -sf $(PWD)/dwz . + cd testsuite-bin; \ + if [ "$@" = "check" ]; then \ + ln -sf $(PWD)/dwz .; \ + else \ + echo "valgrind $(VALGRIND_OPTIONS) $(PWD)/dwz \"\$$@\"" > dwz; \ + chmod +x dwz; \ + fi export DEJAGNU=$(DEJAGNU); \ export PATH=$(PWD)/testsuite-bin:$$PATH; export LC_ALL=C; \ runtest --tool=dwz -srcdir $(srcdir)/testsuite $(RUNTESTFLAGS) - rm -Rf testsuite-bin $(TEST_EXECS) $(DWZ_TEST_SOURCES) + rm -Rf testsuite-bin $(TEST_EXECS) $(DWZ_TEST_OBJECTS) diff --git a/README.release-checklist b/README.release-checklist new file mode 100644 index 0000000..cb6bbad --- /dev/null +++ b/README.release-checklist @@ -0,0 +1,28 @@ +- Verify that copyright notices in source files are up-to-date. +- Update COPYRIGHT_YEARS using contrib/release/gen-copyright-years.sh. + Commit modifications if there are any. +- Run contrib/release/do-release.sh. + Use: + - --minor to do a minor update: $maj.$min -> $maj.$(($min + 1)) + - --major to do a major update: $maj.$min -> $(($maj + 1)).0 + This: + - adds a commit that updates the VERSION file, + - creates a signed annotated release tag for that commit, and + - pushes both the commit and the tag to the remote repository. +- Run contrib/release/upload-release.sh. + This creates and uploads two release tarballs. +- Write draft release announcement. + F.i. using template of + https://sourceware.org/ml/gdb-announce/2019/msg00001.html. +- Sent out draft release announcement to maintainers for review and + further contributions. +- Sent out release announcement. Sent to: + - Maintainers + - dwz@sourceware.org + - dwarf-discuss@lists.dwarfstd.org + - gcc@gcc.gnu.org + - gdb@sourceware.org + - lldb-dev@lists.llvm.org +- Update web page ( https://sourceware.org/dwz/ ): + - Add news item with hlink to release annoucement. + - Add entry in release list. diff --git a/VERSION b/VERSION index 948a547..2856407 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.14 +0.15 diff --git a/args.c b/args.c new file mode 100644 index 0000000..e93c24d --- /dev/null +++ b/args.c @@ -0,0 +1,743 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "args.h" + +#include "util.h" + +#if DEVEL +int tracing; +int ignore_size; +int ignore_locus; +int dump_checksum_p; +int dump_dies_p; +int dump_dups_p; +int dump_pus_p; +int verify_dups_p; +int verify_edge_freelist; +int stats_p; +int checksum_cycle_opt = 1; +int skip_producers_p; +#endif + +int unoptimized_multifile; +int save_temps; +int verify_edges_p; +int dump_edges_p; +int partition_dups_opt; +int progress_p; +int progress_mem_p; +int import_opt_p = 1; +int force_p; +int max_forks = -1; + +enum deduplication_mode deduplication_mode = dm_inter_cu; + +int uni_lang_p = 0; +int gen_cu_p = 0; + +enum die_count_methods die_count_method = estimate; + +int odr = 0; +enum odr_mode odr_mode = ODR_LINK; + +/* Filename if inter-file size optimization should be performed. */ +const char *multifile; + +/* Argument of -M option, i.e. preferred name that should be stored + into the .gnu_debugaltlink or .debug_sup section. */ +const char *multifile_name; + +/* True if -r option is present, i.e. .gnu_debugaltlink or .debug_sup section + should contain a filename relative to the directory in which + the particular file is present. */ +bool multifile_relative; + +/* Pointer size of multifile. */ +int multifile_force_ptr_size; +/* Endianity of multifile. */ +int multifile_force_endian; + +/* True if DWARF 5 .debug_sup and DW_FORM_ref_sup4 / DW_FORM_strp_sup + should be used instead of the GNU extensions .gnu_debugaltlink + and DW_FORM_GNU_ref_alt / DW_FORM_GNU_strp_alt etc. */ +bool dwarf_5; + +/* True if -q option has been passed. */ +bool quiet; + +/* Number of DIEs, above which dwz retries processing + in low_mem mode (and give up on multifile optimizing + the file in question). */ +unsigned int low_mem_die_limit = 10000000; + +/* Number of DIEs, above which dwz gives up processing + input altogether. */ +unsigned int max_die_limit = 50000000; + +/* Phase of multifile handling. */ +unsigned char multifile_mode; + +static int die_count_method_parsed; +static int deduplication_mode_parsed; +static int odr_mode_parsed; +static int skip_producer_parsed; + +/* Options for getopt_long. */ +static struct option dwz_options[] = +{ + { "help", no_argument, 0, '?' }, + { "output", required_argument, 0, 'o' }, + { "multifile", required_argument, 0, 'm' }, + { "quiet", no_argument, 0, 'q' }, + { "hardlink", no_argument, 0, 'h' }, + { "low-mem-die-limit", required_argument, 0, 'l' }, + { "max-die-limit", required_argument, 0, 'L' }, + { "multifile-name", required_argument, 0, 'M' }, + { "relative", no_argument, 0, 'r' }, + { "version", no_argument, 0, 'v' }, + { "import-optimize", + no_argument, &import_opt_p, 1 }, + { "no-import-optimize", + no_argument, &import_opt_p, 0 }, + { "dwarf-5", no_argument, 0, '5' }, +#if DEVEL + { "devel-trace", no_argument, &tracing, 1 }, + { "devel-progress", no_argument, &progress_p, 1 }, + { "devel-progress-mem",no_argument, &progress_mem_p, 1 }, + { "devel-ignore-size", no_argument, &ignore_size, 1 }, + { "devel-ignore-locus",no_argument, &ignore_locus, 1 }, + { "devel-force", no_argument, &force_p, 1 }, + { "devel-save-temps", no_argument, &save_temps, 1 }, + { "devel-dump-checksum", + no_argument, &dump_checksum_p, 1 }, + { "devel-dump-dies", no_argument, &dump_dies_p, 1 }, + { "devel-dump-dups", no_argument, &dump_dups_p, 1 }, + { "devel-dump-pus", no_argument, &dump_pus_p, 1 }, + { "devel-unoptimized-multifile", + no_argument, &unoptimized_multifile, 1 }, + { "devel-verify-edges",no_argument, &verify_edges_p, 1 }, + { "devel-verify-dups", no_argument, &verify_dups_p, 1 }, + { "devel-dump-edges", no_argument, &dump_edges_p, 1 }, + { "devel-partition-dups-opt", + no_argument, &partition_dups_opt, 1 }, + { "devel-die-count-method", + required_argument, &die_count_method_parsed, 1 }, + { "devel-stats", no_argument, &stats_p, 1 }, + { "devel-deduplication-mode", + required_argument, &deduplication_mode_parsed, 1 }, + { "devel-uni-lang", + no_argument, &uni_lang_p, 1 }, + { "devel-no-uni-lang", + no_argument, &uni_lang_p, 0 }, + { "devel-gen-cu", + no_argument, &gen_cu_p, 1 }, + { "devel-no-gen-cu", + no_argument, &gen_cu_p, 0 }, + { "devel-checksum-cycle-opt", + no_argument, &checksum_cycle_opt, 1 }, + { "devel-no-checksum-cycle-opt", + no_argument, &checksum_cycle_opt, 0 }, + { "devel-skip-producer", + required_argument, &skip_producer_parsed, 1}, +#endif + { "odr", no_argument, &odr, 1 }, + { "no-odr", no_argument, &odr, 0 }, + { "odr-mode", required_argument, &odr_mode_parsed, 1 }, + { "multifile-pointer-size", + required_argument, 0, 'p' }, + { "multifile-endian", + required_argument, 0, 'e' }, + { "jobs", required_argument, 0, 'j' }, + { NULL, no_argument, 0, 0 } +}; + +/* Struct describing various usage aspects of a command line option. */ +struct option_help +{ + const char *short_name; + const char *long_name; + const char *argument; + const char *default_value; + const char *msg; +}; + +/* Describe common command line options. */ +static struct option_help dwz_common_options_help[] = +{ + { "q", "quiet", NULL, NULL, + "Silence up the most common messages." }, + { "l", "low-mem-die-limit", "", "10 million DIEs", + "Handle files larger than this limit using a slower and more memory" + " usage friendly mode and don't optimize those files in multifile mode." }, + { "L", "max-die-limit", "", "50 million DIEs", + "Don't optimize files larger than this limit." }, + { NULL, "odr", NULL, NULL, + NULL }, + { NULL, "no-odr", NULL, "Disabled", + "Enable/disable one definition rule optimization." }, + { NULL, "odr-mode", "", "link", + "Set aggressiveness level of one definition rule optimization." }, + { NULL, "import-optimize", NULL, NULL, + NULL }, + { NULL, "no-import-optimize", NULL, "Enabled", + "Enable/disable optimization that reduces the number of" + " DW_TAG_imported_unit DIEs." } +}; + +/* Describe single-file command line options. */ +static struct option_help dwz_single_file_options_help[] = +{ + { "o", "output", "OUTFILE", NULL, + "Place the output in OUTFILE." } +}; + +#if NATIVE_ENDIAN_VAL == ELFDATA2MSB +#define NATIVE_ENDIAN "big" +#elif NATIVE_ENDIAN_VAL == ELFDATA2LSB +#define NATIVE_ENDIAN "little" +#else +#define NATIVE_ENDIAN "not available" +#endif + +/* Describe mult-file command line options. */ +static struct option_help dwz_multi_file_options_help[] = +{ + { "h", "hardlink", NULL, NULL, + "Handle hardlinked files as one file." }, + { "m", "multifile", "COMMONFILE", NULL, + "Enable multifile optimization, placing common DIEs in multifile" + " COMMONFILE." }, + { "M", "multifile-name", "NAME", NULL, + "Set .gnu_debugaltlink or .debug_sup in files to NAME." }, + { "r", "relative", NULL, NULL, + "Set .gnu_debugaltlink in files to relative path from file directory" + " to multifile." }, + { "5", "dwarf-5", NULL, NULL, + "Emit DWARF 5 standardized supplementary object files instead of" + " GNU extension .debug_altlink." }, + { "p", "multifile-pointer-size", "", "auto", + "Set pointer size of multifile, in number of bytes." + " Native pointer size is " XSTR (NATIVE_POINTER_SIZE) "." }, + { "e", "multifile-endian", "", "auto", + "Set endianity of multifile." + " Native endianity is " NATIVE_ENDIAN "." }, + { "j", "jobs", "", "number of processors / 2", + "Process files in parallel." } +}; + +/* Describe misc command line options. */ +static struct option_help dwz_misc_options_help[] = +{ + { "v", "version", NULL, NULL, + "Display dwz version information." }, + { "?", "help", NULL, NULL, + "Display this information." } +}; + +/* Print LEN spaces to STREAM. */ +static void +do_indent (FILE *stream, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + fprintf (stream, " "); +} + +/* Print MSG to STREAM, indenting to INDENT and wrapping at LIMIT. + Assume starting position is at INDENT. */ +static void +wrap (FILE *stream, unsigned int indent, unsigned int limit, const char *msg) +{ + unsigned int len = indent; + const char *s = msg; + while (true) + { + const char *e = strchr (s, ' '); + unsigned int word_len; + if (e == NULL) + word_len = strlen (s); + else + word_len = e - s; + if (word_len == 0) + return; + + if (len + 1 /* space */ + word_len > limit) + { + fprintf (stream, "\n"); + do_indent (stream ,indent); + len = indent; + } + else if (len > indent) + { + fprintf (stream, " "); + len += 1; + } + + if (e != NULL) + { + const char *i; + for (i = s; i < e; ++i) + fprintf (stream, "%c", *i); + } + else + fprintf (stream, "%s", s); + len += word_len; + + if (e == NULL) + break; + + s = e + 1; + } +} + +/* Print OPTIONS_HELP of length H to STREAM, indenting to help message to + INDENT an wrapping at LIMIT. */ +static void +print_options_help (FILE *stream, struct option_help *options_help, unsigned int n, + unsigned int indent, unsigned int limit) +{ + unsigned len; + const char *s; + unsigned int i; + + for (i = 0; i < n; ++i) + { + len = 0; + + fprintf (stream, " "); + len += 2; + + s = options_help[i].short_name; + if (s) + { + fprintf (stream, "-%s", s); + len += 2; + } + + s = options_help[i].long_name; + if (len == 4) + { + fprintf (stream, ", "); + len += 2; + } + fprintf (stream, "--%s", s); + len += 2 + strlen (s); + + s = options_help[i].argument; + if (s) + { + fprintf (stream, " %s", s); + len += 1 + strlen (s); + } + + s = options_help[i].msg; + if (s) + { + assert (IMPLIES (strlen (s) > 0, s[strlen (s) - 1] == '.')); + if (len > indent) + { + fprintf (stream, "\n"); + do_indent (stream, indent); + } + else + do_indent (stream, indent - len); + len = indent; + + wrap (stream, indent, limit, s); + } + fprintf (stream, "\n"); + + s = options_help[i].default_value; + if (s) + { + do_indent (stream, indent); + fprintf (stream, "Default value: %s.\n", s); + } + } +} + +/* Print usage and exit. */ +static void +usage (int failing) +{ + unsigned int n, i; + unsigned int indent, limit; + FILE *stream = failing ? stderr : stdout; + const char *header_lines[] = { + "dwz [common options] [-h] [-m COMMONFILE] [-M NAME | -r] [-5]", + " [-p ] [-e ] [-j N] [FILES]", + "dwz [common options] -o OUTFILE FILE", + "dwz [ -v | -? ]" + }; + unsigned int nr_header_lines + = sizeof (header_lines) / sizeof (*header_lines); + + fprintf (stream, "Usage:\n"); + for (i = 0; i < nr_header_lines; ++i) + fprintf (stream, " %s\n", header_lines[i]); + + indent = 30; + limit = 80; + fprintf (stream, "Common options:\n"); + n = (sizeof (dwz_common_options_help) + / sizeof (dwz_common_options_help[0])); + print_options_help (stream, dwz_common_options_help, n, indent, limit); + + fprintf (stream, "Single-file options:\n"); + n = (sizeof (dwz_single_file_options_help) + / sizeof (dwz_single_file_options_help[0])); + print_options_help (stream, dwz_single_file_options_help, n, indent, limit); + + fprintf (stream, "Multi-file options:\n"); + n = (sizeof (dwz_multi_file_options_help) + / sizeof (dwz_multi_file_options_help[0])); + print_options_help (stream, dwz_multi_file_options_help, n, indent, limit); + + fprintf (stream, "Miscellaneous options:\n"); + n = (sizeof (dwz_misc_options_help) + / sizeof (dwz_misc_options_help[0])); + print_options_help (stream, dwz_misc_options_help, n, indent, limit); + +#if DEVEL + fprintf (stream, "Development options:\n"); + fprintf (stream, "%s", + (" --devel-trace\n" + " --devel-progress\n" + " --devel-progress-mem\n" + " --devel-stats\n" + " --devel-ignore-size\n" + " --devel-ignore-locus\n" + " --devel-force\n" + " --devel-save-temps\n" + " --devel-dump-checksum\n" + " --devel-dump-dies\n" + " --devel-dump-dups\n" + " --devel-dump-pus\n" + " --devel-unoptimized-multifile\n" + " --devel-verify-dups\n" + " --devel-verify-edges\n" + " --devel-dump-edges\n" + " --devel-partition-dups-opt\n" + " --devel-die-count-method\n" + " --devel-deduplication-mode={none,intra-cu,inter-cu}\n" + " --devel-uni-lang / --devel-no-uni-lang\n" + " --devel-gen-cu / --devel-no-gen-cu\n" + " --devel-skip-producer \n")); +#endif + + exit (failing); +} + +/* Print version and exit. */ +static void +version (void) +{ + printf ("dwz version " DWZ_VERSION "\n" + "Copyright (C) " RH_YEARS " Red Hat, Inc.\n" + "Copyright (C) " FSF_YEARS " Free Software Foundation, Inc.\n" + "Copyright (C) " SUSE_YEARS " SUSE LLC.\n" + "This program is free software; you may redistribute it under the terms of\n" + "the GNU General Public License version 3 or (at your option) any later version.\n" + "This program has absolutely no warranty.\n"); + exit (0); +} + +static const char **skip_producers; +static size_t skip_producers_size; +static size_t nr_skip_producers; + +static void +add_skip_producer (const char *producer) +{ + size_t alloc_size; + if (skip_producers == NULL) + { + skip_producers_size = 10; + alloc_size = skip_producers_size * sizeof (const char *); + skip_producers = malloc (alloc_size); + } + else if (nr_skip_producers == skip_producers_size) + { + skip_producers_size += 10; + alloc_size = skip_producers_size * sizeof (const char *); + skip_producers = realloc (skip_producers, alloc_size); + } + + skip_producers[nr_skip_producers] = producer; + nr_skip_producers++; +} + +bool +skip_producer (const char *producer) +{ + size_t i; + + if (producer == NULL) + return false; + + for (i = 0; i < nr_skip_producers; ++i) + { + const char *skip = skip_producers[i]; + if (strncmp (skip, producer, strlen (skip)) == 0) + return true; + } + + return false; +} + +/* Parse command line arguments in ARGV. */ +void +parse_args (int argc, char *argv[], bool *hardlink, const char **outfile) +{ + unsigned long l; + char *end; + + while (1) + { + int option_index = -1; + int c = getopt_long (argc, argv, "m:o:qhl:L:M:r?v5p:e:j:", dwz_options, + &option_index); + if (c == -1) + break; + switch (c) + { + default: + case '?': + usage (option_index == -1); + break; + + case 0: + /* Option handled by getopt_long. */ + if (die_count_method_parsed) + { + die_count_method_parsed = 0; + if (strcmp (optarg, "none") == 0) + { + die_count_method = none; + break; + } + if (strcmp (optarg, "estimate") == 0) + { + die_count_method = estimate; + break; + } + error (1, 0, "invalid argument --devel-die-count-method %s", + optarg); + } + if (deduplication_mode_parsed) + { + deduplication_mode_parsed = 0; + if (strcmp (optarg, "none") == 0) + { + deduplication_mode = dm_none; + break; + } + if (strcmp (optarg, "intra-cu") == 0) + { + deduplication_mode = dm_intra_cu; + break; + } + if (strcmp (optarg, "inter-cu") == 0) + { + deduplication_mode = dm_inter_cu; + break; + } + error (1, 0, "invalid argument --devel-deduplication-mode %s", + optarg); + } + if (odr_mode_parsed) + { + odr_mode_parsed = 0; + if (strcmp (optarg, "basic") == 0) + { + odr_mode = ODR_BASIC; + break; + } + if (strcmp (optarg, "link") == 0) + { + odr_mode = ODR_LINK; + break; + } + error (1, 0, "invalid argument --odr-mode %s", + optarg); + } + if (skip_producer_parsed) + { + skip_producer_parsed = 0; + add_skip_producer (optarg); + +#if DEVEL + skip_producers_p = 1; +#endif + } + break; + + case 'o': + *outfile = optarg; + break; + + case 'm': + multifile = optarg; + break; + + case 'q': + quiet = true; + break; + + case 'h': + *hardlink = true; + break; + + case 'M': + multifile_name = optarg; + break; + + case 'r': + multifile_relative = true; + break; + + case 'l': + if (strcmp (optarg, "none") == 0) + { + low_mem_die_limit = -1U; + break; + } + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -l %s", optarg); + low_mem_die_limit = l; + break; + + case 'L': + if (strcmp (optarg, "none") == 0) + { + max_die_limit = -1U; + break; + } + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -L %s", optarg); + max_die_limit = l; + break; + + case '5': + dwarf_5 = true; + break; + + case 'p': + if (strcmp (optarg, "auto") == 0) + { + multifile_force_ptr_size = 0; + break; + } + if (strcmp (optarg, "native") == 0) + { + multifile_force_ptr_size = NATIVE_POINTER_SIZE; + break; + } + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -l %s", optarg); + multifile_force_ptr_size = l; + break; + + case 'e': + if (strcmp (optarg, "auto") == 0) + { + multifile_force_endian = 0; + break; + } + if (strcmp (optarg, "native") == 0) + { + switch (NATIVE_ENDIAN_VAL) + { + case ELFDATA2MSB: + case ELFDATA2LSB: + multifile_force_endian = NATIVE_ENDIAN_VAL; + break; + default: + error (1, 0, "Cannot determine native endian"); + } + break; + } + if (strlen (optarg) != 1) + error (1, 0, "invalid argument -l %s", optarg); + switch (optarg[0]) + { + case 'l': + case 'L': + multifile_force_endian = ELFDATA2LSB; + break; + case 'b': + case 'B': + multifile_force_endian = ELFDATA2MSB; + break; + default: + error (1, 0, "invalid argument -l %s", optarg); + } + break; + + case 'v': + version (); + break; + + case 'j': + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -j %s", optarg); + max_forks = l; + break; + } + } + + if (progress_mem_p) + progress_p = 1; + + /* Specifying a low-mem die-limit that is larger than or equal to the + max die-limit has the effect of disabling low-mem mode. Make this + explicit by setting it to the 'none' value. */ + if (low_mem_die_limit != -1U + && low_mem_die_limit >= max_die_limit) + low_mem_die_limit = -1U; + + if (multifile_relative && multifile_name) + error (1, 0, "-M and -r options can't be specified together"); + + if (max_forks == -1) + { + long nprocs = get_nprocs (); + /* Be conservative on max forks: 4 procs may be actually be 4 SMT + threads with only 2 cores. */ + max_forks = nprocs / 2; + } +} diff --git a/args.h b/args.h new file mode 100644 index 0000000..0eacbd0 --- /dev/null +++ b/args.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek , 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. */ + +#if DEVEL +extern int tracing; +extern int ignore_size; +extern int ignore_locus; +extern int dump_checksum_p; +extern int dump_dies_p; +extern int dump_dups_p; +extern int dump_pus_p; +extern int verify_dups_p; +extern int verify_edge_freelist; +extern int stats_p; +extern int checksum_cycle_opt; +extern int skip_producers_p; +#else +#define tracing 0 +#define ignore_size 0 +#define ignore_locus 0 +#define dump_checksum_p 0 +#define dump_dies_p 0 +#define dump_dups_p 0 +#define dump_pus_p 0 +#define verify_dups_p 0 +#define stats_p 0 +#define checksum_cycle_opt 1 +#define skip_producers_p 0 +#endif + +extern int unoptimized_multifile; +extern int save_temps; +extern int verify_edges_p; +extern int dump_edges_p; +extern int partition_dups_opt; +extern int progress_p; +extern int progress_mem_p; +extern int import_opt_p; +extern int force_p; +extern int max_forks; + +enum deduplication_mode +{ + dm_none, + dm_intra_cu, + dm_inter_cu +}; +extern enum deduplication_mode deduplication_mode; + +extern int uni_lang_p; +extern int gen_cu_p; + +enum die_count_methods +{ + none, + estimate +}; +extern enum die_count_methods die_count_method; + +extern int odr; +enum odr_mode { ODR_BASIC, ODR_LINK }; +extern enum odr_mode odr_mode; + +extern const char *multifile; +extern const char *multifile_name; +extern bool multifile_relative; +extern int multifile_force_ptr_size; +extern int multifile_force_endian; + +extern unsigned char multifile_mode; + +extern bool dwarf_5; + +extern bool quiet; + +extern unsigned int low_mem_die_limit; +extern unsigned int max_die_limit; + +extern void parse_args (int, char *[], bool *, const char **); +extern bool skip_producer (const char *producer); diff --git a/contrib/bytes-per-die.sh b/contrib/bytes-per-die.sh index 969f733..fda8998 100755 --- a/contrib/bytes-per-die.sh +++ b/contrib/bytes-per-die.sh @@ -3,7 +3,7 @@ f="$1" size=$(readelf -WS "$f" \ - | egrep "[ \t]\.debug_info" \ + | grep -E "[ \t]\.debug_info" \ | sed 's/.*\.debug_info//' \ | awk '{print $4}') size=$((16#$size)) diff --git a/contrib/release/do-release.sh b/contrib/release/do-release.sh index f1bb4d9..1127293 100755 --- a/contrib/release/do-release.sh +++ b/contrib/release/do-release.sh @@ -52,10 +52,16 @@ echo Bumped version: major: $major, minor: $minor version=$major.$minor +set +x + echo $version > VERSION git add VERSION git commit -m "Bump version to $version" -git tag dwz-$version +git push origin master:master + +git tag -s -m "dwz $version release" dwz-$version + +git push origin dwz-$version diff --git a/contrib/release/gen-copyright-years.sh b/contrib/release/gen-copyright-years.sh index d081cd2..f97691a 100755 --- a/contrib/release/gen-copyright-years.sh +++ b/contrib/release/gen-copyright-years.sh @@ -128,6 +128,8 @@ main () tmp=$(mktemp) for f in *.c *.h *.def; do + if test "$f" = "native.c"; then continue; fi + if ! grep -q "Copyright (C)" $f; then echo "error: found file without copyright marker: $f" exit 1 diff --git a/contrib/release/upload-release.sh b/contrib/release/upload-release.sh index e5f6d70..3c2abdd 100755 --- a/contrib/release/upload-release.sh +++ b/contrib/release/upload-release.sh @@ -4,7 +4,7 @@ set -e pwd=$(pwd -P) -version="$1" +version=$(cat VERSION) tag=dwz-$version rootdir=dwz diff --git a/dwz.1 b/dwz.1 index 19dc814..1cff329 100644 --- a/dwz.1 +++ b/dwz.1 @@ -77,6 +77,16 @@ the executable or shared library to the file named in the argument of the \fB-m\fR option. Either \fB-M\fR or \fB-r\fR option can be specified, but not both. .TP +.B \-p N \-\-multifile-pointer-size +Specify the pointer size of the multifile, in bytes. If auto, use the +pointer size of the files, provided they match. If native, use native pointer +size, as specified in the help message. +.TP +.B \-p \-\-multifile-endian +Specify the endianity of the multifile. If auto, use the endianity of +the files, provided they match. If native, use native endianity, as specified +in the help message. +.TP .B \-q \-\-quiet Silence up some of the most common messages. .TP @@ -111,6 +121,10 @@ Emit standard DWARF 5 Supplementary Object Files with \fI.debug_sup\fR and corresponding forms, instead of the GNU extension \fI.gnu_debugaltlink\fR and corresponding forms. .TP +.B \-j \-\-jobs +Process \fIN\fR files in parallel. The default is processors / 2. Disabled +when multifile is used. +.TP .B \-\-odr / \-\-no-odr .B Experimental. Enable/disable One-Definition-Rule optimization for C++ compilation units. diff --git a/dwz.c b/dwz.c index 5effbc1..3bc6038 100644 --- a/dwz.c +++ b/dwz.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -35,13 +34,19 @@ #include #include #include +#include #include #include +#include + #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 @@ -109,29 +114,41 @@ # define NT_GNU_BUILD_ID 3 #endif -#if defined __GNUC__ && __GNUC__ >= 3 -# define likely(x) __builtin_expect (!!(x), 1) -# define unlikely(x) __builtin_expect (!!(x), 0) -#else -# define likely(x) (x) -# define unlikely(x) (x) -#endif +/* xxHash state object. Init in main. */ +static XXH64_state_t *state; -#if defined __GNUC__ -# define FORCE_INLINE __attribute__((always_inline)) -# define UNUSED __attribute__((unused)) -# define USED __attribute__((used)) -#else -# define FORCE_INLINE -# define UNUSED -# define USED -#endif +/* Clear xxHash state to zero. */ +#define hash_init_state() XXH64_reset(state, 0) -/* Utility macro. */ -#define IMPLIES(A, B) (!((A) && !(B))) -#define MAX(A, B) ((A) > (B) ? (A) : (B)) -#define MIN(A, B) ((A) < (B) ? (A) : (B)) +/* 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) @@ -157,6 +174,40 @@ report_progress (void) 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 @@ -168,7 +219,7 @@ 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. */ -static void +void dwz_oom (void) { longjmp (oom_buf, 1); @@ -184,57 +235,7 @@ static struct obstack ob2; and restored during final cleanup. */ static struct obstack alt_ob, alt_ob2; -#if DEVEL -static int tracing; -static int ignore_size; -static int ignore_locus; -static int dump_checksum_p; -static int dump_dies_p; -static int dump_dups_p; -static int dump_pus_p; -static int verify_dups_p; -static int verify_edge_freelist; -static int stats_p; -#else -#define tracing 0 -#define ignore_size 0 -#define ignore_locus 0 -#define dump_checksum_p 0 -#define dump_dies_p 0 -#define dump_dups_p 0 -#define dump_pus_p 0 -#define verify_dups_p 0 -#define stats_p 0 -#endif -static int unoptimized_multifile; -static int save_temps = 0; -static int verify_edges_p = 0; -static int dump_edges_p = 0; -static int partition_dups_opt; -static int progress_p; -static int import_opt_p = 1; -static int force_p = 0; -enum deduplication_mode -{ - dm_none, - dm_intra_cu, - dm_inter_cu -}; -static enum deduplication_mode deduplication_mode = dm_inter_cu; -static int uni_lang_p = 0; -static int gen_cu_p = 0; -enum die_count_methods -{ - none, - estimate -}; -static enum die_count_methods die_count_method = estimate; - -int odr = 0; -enum odr_mode { ODR_BASIC, ODR_LINK }; -enum odr_mode odr_mode = ODR_LINK; -int odr_mode_parsed = 0; -bool odr_active_p = false; +static bool odr_active_p = false; /* Struct to gather statistics. */ struct stats @@ -253,7 +254,7 @@ struct stats unsigned int pu_ph2_cnt; unsigned int pu_toplevel_die_cnt; }; -struct stats *stats; +static struct stats *stats; /* Initialize stats struct. */ static void @@ -517,13 +518,15 @@ buf_read_ube16 (unsigned char *data) static inline uint32_t buf_read_ule32 (unsigned char *data) { - return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + 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) | (data[0] << 24); + return (data[3] | (data[2] << 8) | (data[1] << 16) + | ((unsigned int)data[0] << 24)); } static inline uint64_t @@ -828,18 +831,6 @@ static int multi_endian; /* Highest .gdb_index version seen. */ static unsigned int multi_gdb_index_ver; -/* Number of DIEs, above which dwz retries processing - in low_mem mode (and give up on multifile optimizing - the file in question). */ -static unsigned int low_mem_die_limit = 10000000; - -/* Number of DIEs, above which dwz gives up processing - input altogether. */ -static unsigned int max_die_limit = 50000000; - -/* Phase of multifile handling. */ -static unsigned char multifile_mode; - enum multifile_mode_kind { MULTIFILE_MODE_WR = 1, @@ -864,29 +855,9 @@ enum multifile_mode_kind /* True if running in low_mem mode. */ #define low_mem (multifile_mode & MULTIFILE_MODE_LOW_MEM) -/* Filename if inter-file size optimization should be performed. */ -static const char *multifile; - -/* Argument of -M option, i.e. preferred name that should be stored - into the .gnu_debugaltlink or .debug_sup section. */ -static const char *multifile_name; - -/* True if -r option is present, i.e. .gnu_debugaltlink or .debug_sup section - should contain a filename relative to the directory in which - the particular file is present. */ -static bool multifile_relative; - /* SHA1 checksum (build-id) of the common file. */ static unsigned char multifile_sha1[0x14]; -/* True if DWARF 5 .debug_sup and DW_FORM_ref_sup4 / DW_FORM_strp_sup - should be used instead of the GNU extensions .gnu_debugaltlink - and DW_FORM_GNU_ref_alt / DW_FORM_GNU_strp_alt etc. */ -static bool dwarf_5; - -/* True if -q option has been passed. */ -static bool quiet; - /* A single attribute in abbreviations. */ struct abbrev_attr { @@ -1151,79 +1122,17 @@ die_cu (dw_die_ref die) #define die_safe_nextdup(die) \ ((die)->die_toplevel ? (die)->die_nextdup : (dw_die_ref) NULL) -#ifdef __GNUC__ -# define ALIGN_STRUCT(name) -#else -# define ALIGN_STRUCT(name) struct align_##name { char c; struct name s; }; -#endif ALIGN_STRUCT (abbrev_tag) ALIGN_STRUCT (dw_file) ALIGN_STRUCT (dw_cu) ALIGN_STRUCT (dw_die) -/* Big pool allocator. obstack isn't efficient, because it aligns everything - too much, and allocates too small chunks. All these objects are only freed - together. */ - -/* Pointer to the start of the current pool chunk, current first free byte - in the chunk and byte after the end of the current pool chunk. */ -static unsigned char *pool, *pool_next, *pool_limit; /* 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; -/* Allocate SIZE bytes with ALIGN bytes alignment from the pool. */ -static void * -pool_alloc_1 (unsigned int align, unsigned int size) -{ - void *ret; - if (pool == NULL - || (size_t) (pool_limit - pool_next) < (size_t) align + size) - { - size_t new_size = (size_t) align + size; - unsigned char *new_pool; - new_size += sizeof (void *); - if (new_size < 16384 * 1024 - 64) - new_size = 16384 * 1024 - 64; - new_pool = (unsigned char *) malloc (new_size); - if (new_pool == NULL) - dwz_oom (); - *(unsigned char **) new_pool = pool; - pool_next = new_pool + sizeof (unsigned char *); - pool_limit = new_pool + new_size; - pool = new_pool; - } - pool_next = (unsigned char *) (((uintptr_t) pool_next + align - 1) - & ~(uintptr_t) (align - 1)); - ret = pool_next; - pool_next += size; - return ret; -} - -/* Free the whole pool. */ -static void -pool_destroy (void) -{ - pool_next = NULL; - pool_limit = NULL; - while (pool) - { - void *p = (void *) pool; - pool = *(unsigned char **) pool; - free (p); - } -} - -#ifdef __GNUC__ -# define pool_alloc(name, size) \ - (struct name *) pool_alloc_1 (__alignof__ (struct name), size) -#else -# define pool_alloc(name, size) \ - (struct name *) pool_alloc_1 (offsetof (struct align_##name, s), size) -#endif - static struct abbrev_tag * pool_clone_abbrev (struct abbrev_tag *t) { @@ -1300,16 +1209,18 @@ compute_abbrev_hash (struct abbrev_tag *t) { unsigned int i; - t->hash = iterative_hash_object (t->tag, 0); - t->hash = iterative_hash_object (t->nattr, t->hash); - t->hash = iterative_hash_object (t->children, t->hash); + 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++) { - t->hash = iterative_hash_object (t->attr[i].attr, t->hash); - t->hash = iterative_hash_object (t->attr[i].form, t->hash); + 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) - t->hash = iterative_hash_object (t->values[i], t->hash); + hash_update_state_object (t->values[i]); } + t->hash = hash_digest (); } /* Maximum number of attributes in a DIE. */ @@ -1351,8 +1262,9 @@ read_abbrev (DSO *dso, unsigned char *ptr) || form == DW_FORM_data16 || form == DW_FORM_line_strp))) { - error (0, 0, "%s: Unknown DWARF %s", - dso->filename, get_DW_FORM_str (form)); + 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; } @@ -1882,7 +1794,7 @@ read_debug_line (DSO *dso, dw_cu_ref cu, uint32_t off) { case DW_FORM_string: f = (char *) ptr; - end = strchr ((char *)ptr, 0) + 1;; + end = strchr ((char *)ptr, 0) + 1; break; case DW_FORM_strp: { @@ -1992,7 +1904,7 @@ read_debug_line (DSO *dso, dw_cu_ref cu, uint32_t off) /* Estimate the amount of DIEs in the .debug_info section, based on the size of that section. */ -static unsigned int UNUSED +static unsigned int estimate_nr_dies (void) { unsigned int average_die_size = 11; @@ -2000,7 +1912,7 @@ estimate_nr_dies (void) return nr_dies; } -static size_t UNUSED +static size_t emulate_htab (size_t initial, size_t final_nr_elements) { size_t size = initial; @@ -2574,8 +2486,10 @@ read_exprloc (DSO *dso, dw_die_ref die, unsigned char *ptr, size_t len, *need_adjust = true; break; default: - error (0, 0, "%s: Unknown DWARF %s", - dso->filename, get_DW_OP_str (op)); + error (0, 0, "%s: Unknown DWARF %s " + "referenced from DIE at [%x]", + dso->filename, get_DW_OP_str (op), + die->die_offset); return 1; } } @@ -2776,8 +2690,10 @@ read_exprloc_low_mem_phase1 (DSO *dso, dw_die_ref die, unsigned char *ptr, skip_leb128 (ptr); break; default: - error (0, 0, "%s: Unknown DWARF %s", - dso->filename, get_DW_OP_str (op)); + error (0, 0, "%s: Unknown DWARF %s " + "referenced from DIE at [%x]", + dso->filename, get_DW_OP_str (op), + die->die_offset); return 1; } } @@ -3310,7 +3226,7 @@ set_die_odr_state (dw_cu_ref cu, dw_die_ref die) } /* Return the initialized die_odr_state field for DIE with CU. */ -static unsigned int UNUSED +static unsigned int die_odr_state (dw_die_ref die) { assert (die->die_odr_state != ODR_UNKNOWN); @@ -3391,6 +3307,7 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) size_t len = 0; unsigned char *old_ptr; bool handled = false; + int64_t svalue; uint64_t value; while (form == DW_FORM_indirect) @@ -3464,7 +3381,7 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) value = read_uleb128 (ptr); handled = true; break; case DW_FORM_sdata: { - int64_t svalue = read_sleb128 (ptr); + svalue = read_sleb128 (ptr); if (svalue >= 0) { value = svalue; @@ -3482,9 +3399,10 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) } case DW_FORM_implicit_const: { - if (t->values[i] >= 0) + svalue = t->values[i]; + if (svalue >= 0) { - value = t->values[i]; + value = svalue; handled = true; break; } @@ -3515,22 +3433,17 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) struct dw_file *cu_file = &cu->cu_files[value - 1]; size_t file_len = strlen (cu_file->file); s = t->attr[i].attr; - die->u.p1.die_hash - = iterative_hash_object (s, die->u.p1.die_hash); - die->u.p1.die_hash - = iterative_hash_object (cu_file->time, - die->u.p1.die_hash); - die->u.p1.die_hash - = iterative_hash_object (cu_file->size, - die->u.p1.die_hash); - die->u.p1.die_hash - = iterative_hash (cu_file->file, file_len + 1, - die->u.p1.die_hash); + 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) - die->u.p1.die_hash - = iterative_hash (cu_file->dir, - strlen (cu_file->dir) + 1, - die->u.p1.die_hash); + { + hash_update_state (cu_file->dir, + strlen (cu_file->dir) + 1); + } /* Ignore DW_AT_comp_dir for DW_AT_*_file etc. if immediately followed by DW_AT_*_line 0. */ else if (cu_file->file_angle_brackets_encapsulated_no_slash @@ -3540,7 +3453,13 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) ? DW_AT_decl_line : DW_AT_call_line) && t->attr[i + 1].form == DW_FORM_data1 && *new_ptr == 0) - break; + { + 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]) != '/') @@ -3570,7 +3489,7 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) value = read_uleb128 (ptr); handled = true; break; case DW_FORM_sdata: { - int64_t svalue = read_sleb128 (ptr); + svalue = read_sleb128 (ptr); if (svalue >= 0) { value = svalue; @@ -3581,9 +3500,10 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) goto negative; } case DW_FORM_implicit_const: - if (t->values[i] >= 0) + svalue = t->values[i]; + if (svalue >= 0) { - value = t->values[i]; + value = svalue; handled = true; break; } @@ -4380,7 +4300,7 @@ checksum_ref_die (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, } } } - if (minidx != -1U) + if (checksum_cycle_opt && minidx != -1U) { idx = 0; checksum_ref_die (die_cu (arr[minidx]), arr[minidx], @@ -5544,7 +5464,7 @@ dump_die_with_indent (int indent, dw_die_ref die) } /* Dump DIE to stderr. */ -void USED +static void dump_die (dw_die_ref die) { dump_die_with_indent (0, die); @@ -5654,7 +5574,7 @@ strp_eq2 (const void *p, const void *q) static hashval_t strp_hash3 (const void *p) { - return iterative_hash (p, strlen (p), 0); + return hash (p, strlen (p)); } /* Corresponding equality function in strp_htab. */ @@ -5684,7 +5604,7 @@ note_strp_offset (unsigned int off) p = debug_sections[DEBUG_STR].data + off; len = strlen ((char *) p); - hash = iterative_hash (p, len, 0); + hash = hash (p, len); if (alt_strp_htab) { if (htab_find_with_hash (alt_strp_htab, p, hash)) @@ -5766,7 +5686,7 @@ lookup_strp_offset (unsigned int off) p = debug_sections[DEBUG_STR].data + off; len = strlen ((char *) p); - hash = iterative_hash (p, len, 0); + hash = hash (p, len); if (alt_strp_htab) { unsigned char *q = (unsigned char *) @@ -5807,7 +5727,7 @@ note_strp_offset2 (unsigned int off) { p = debug_sections[DEBUG_STR].data + off; len = strlen ((char *) p); - hash = iterative_hash (p, len, 0); + 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; } @@ -5817,7 +5737,7 @@ note_strp_offset2 (unsigned int off) return DW_FORM_strp; p = debug_sections[DEBUG_STR].data + off; q = (unsigned char *) strchr ((char *) p, '\0'); - hash = iterative_hash (p, q - p, 0); + hash = hash (p, q - p); se.off = off; se.new_off = hash & ~1U; struct strp_entry *s = (struct strp_entry *) @@ -5973,7 +5893,7 @@ finalize_strp (bool build_tail_offset_list) memcpy (p, debug_sections[DEBUG_STR].data + arr[i]->off, len); slot = htab_find_slot_with_hash (strp_htab, p, - iterative_hash (p, len - 1, 0), + hash (p, len - 1), INSERT); if (slot == NULL) dwz_oom (); @@ -5989,7 +5909,7 @@ finalize_strp (bool build_tail_offset_list) if (tail_offset_list != NULL) tail_offset_list[k++] = arr[j]->new_off; slot = htab_find_slot_with_hash (strp_htab, q, - iterative_hash (q, l - 1, 0), + hash (q, l - 1), INSERT); if (slot == NULL) dwz_oom (); @@ -6659,7 +6579,8 @@ read_debug_info (DSO *dso, int kind, unsigned int *die_count) if (dup_htab == NULL) dwz_oom (); } - if (unlikely (op_multifile || rd_multifile || fi_multifile || low_mem)) + 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, @@ -7179,15 +7100,16 @@ read_debug_info (DSO *dso, int kind, unsigned int *die_count) case DW_FORM_block: abort (); default: - error (0, 0, "%s: Unknown DWARF %s", - dso->filename, get_DW_FORM_str (form)); + 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", - dso->filename); + error (0, 0, "%s: Attributes extend beyond end of CU " + "for DIE [%x]", + dso->filename, die_offset); goto fail; } @@ -7195,8 +7117,9 @@ read_debug_info (DSO *dso, int kind, unsigned int *die_count) { if (len >= (size_t) (endcu - ptr)) { - error (0, 0, "%s: Attributes extend beyond end of CU", - dso->filename); + error (0, 0, "%s: Attributes extend beyond end of CU " + "for DIE [%x]", + dso->filename, die_offset); goto fail; } @@ -7210,8 +7133,9 @@ read_debug_info (DSO *dso, int kind, unsigned int *die_count) > DW_AT_GNU_call_site_target_clobbered)) { error (0, 0, "%s: Unknown DWARF %s with " - "block DW_FORM", - dso->filename, get_DW_AT_str (t->attr[i].attr)); + "block DW_FORM for DIE [%x]", + dso->filename, get_DW_AT_str (t->attr[i].attr), + die_offset); goto fail; } @@ -7265,6 +7189,12 @@ read_debug_info (DSO *dso, int kind, unsigned int *die_count) } 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); @@ -8195,12 +8125,13 @@ partition_dups_1 (dw_die_ref *arr, size_t nr_partitions, size_t *partitions, else stats->pu_ph2_cnt++; } - if (dump_pus_p) - fprintf (stderr, "Partial unit (%s):\n", - second_phase ? "phase two" : "phase one"); 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; @@ -8378,7 +8309,12 @@ merged_singleton (dw_die_ref die) { case ODR_DEF: if (res) - return NULL; + { + if (die_cu (res) == die_cu (d)) + continue; + else + return NULL; + } else res = d; break; @@ -8904,7 +8840,7 @@ verify_edges_1 (struct import_cu *ipu, unsigned int *ic, unsigned int *oc, /* Helper function for debugging create_import_tree. Call verify_edges_1 on all CUs and PUs. */ -void +static void verify_edges (struct import_cu **ipus, unsigned int npus, unsigned int ncus, unsigned int phase) { @@ -8976,7 +8912,7 @@ bitvector_set_bit (BITVECTOR_TYPE *vector, unsigned idx) { unsigned div = idx / (sizeof (BITVECTOR_TYPE) * 8); unsigned mod = idx % (sizeof (BITVECTOR_TYPE) * 8); - vector[div] |= (1 << mod); + vector[div] |= (1U << mod); } /* Test bit IDX in bitvector VECTOR. */ @@ -8985,7 +8921,7 @@ 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] & (1 << mod)) != 0; + return (vector[div] & (1U << mod)) != 0; } /* Clear at least bits [A, B] in VECTOR, possibly more. */ @@ -9075,11 +9011,18 @@ create_import_tree (void) 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 (diecu == prev_cu) + 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); @@ -9089,11 +9032,18 @@ create_import_tree (void) 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 (diecu == prev_cu) + 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) @@ -9935,17 +9885,16 @@ line_htab_lookup (dw_cu_ref cu, unsigned int id) { void **slot; struct line_entry le; - hashval_t h; - if (id == 0) return 0; assert (id <= cu->cu_nfiles); le.file = &cu->cu_files[id - 1]; - h = iterative_hash_object (le.file->time, 0); - h = iterative_hash_object (le.file->size, h); - h = iterative_hash (le.file->file, strlen (le.file->file) + 1, h); + 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) - h = iterative_hash (le.file->dir, strlen (le.file->dir) + 1, h); + 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); @@ -9953,15 +9902,15 @@ line_htab_lookup (dw_cu_ref cu, unsigned int id) dwz_oom (); max_line_id = 1; } - le.hash = h; - slot = htab_find_slot_with_hash (line_htab, &le, h, INSERT); + 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 = h; + l->hash = le.hash; l->new_id = max_line_id++; *slot = (void *) l; return l->new_id; @@ -10433,7 +10382,7 @@ handle_macro (void) /* This should only happen if there were multiple same transparent units within a single object file. */ && htab_find_with_hash (strp_htab, p, - iterative_hash (p, len, 0)) == NULL) + hash (p, len)) == NULL) can_share = false; s = ptr; break; @@ -11238,7 +11187,10 @@ build_abbrevs (dw_cu_ref cu, struct abbrev_tag *t, unsigned int *ndies, if (build_abbrevs_for_die (h, cu, cu->cu_die, NULL, NULL, t, ndies, vec, false)) - return 1; + { + htab_delete (h); + return 1; + } cu->cu_new_abbrev = h; return 0; @@ -14443,7 +14395,7 @@ cleanup (void) memset (&ob, '\0', sizeof (ob2)); die_nontoplevel_freelist = NULL; die_collapsed_child_freelist = NULL; - pool_destroy (); + pool_destroy (NULL); first_cu = NULL; last_cu = NULL; ptr_size = 0; @@ -15040,10 +14992,26 @@ clear_p2_field (void) } #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 (DSO *dso) +write_multifile_1 (DSO *dso, struct file_result *res) { dw_cu_ref cu; bool any_cus = false; @@ -15059,18 +15027,50 @@ write_multifile (DSO *dso) if (multi_ehdr.e_ident[0] == '\0') multi_ehdr = dso->ehdr; - if ((multi_ptr_size && ptr_size != multi_ptr_size) - || (multi_endian - && multi_endian != (do_read_32 == buf_read_ule32 - ? ELFDATA2LSB : ELFDATA2MSB))) + 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 or endianity"); + " pointer sizes"); multifile = NULL; return 1; } - multi_ptr_size = ptr_size; - multi_endian = do_read_32 == buf_read_ule32 ? ELFDATA2LSB : ELFDATA2MSB; + 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 (); @@ -15096,10 +15096,15 @@ write_multifile (DSO *dso) dw_cu_ref *cup; for (cup = &first_cu; *cup && (*cup)->cu_kind != CU_TYPES; ) - if ((*cup)->cu_die->die_no_multifile == 0) - cup = &(*cup)->cu_next; - else - *cup = (*cup)->cu_next; + { + 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) @@ -15177,6 +15182,64 @@ write_multifile (DSO *dso) 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 @@ -15252,33 +15315,21 @@ remove_empty_pus (void) return 0; } -/* Helper structure for hardlink discovery. */ -struct file_result -{ - /* -2: Already processed under different name. - -1: Ignore. - 0: Processed, changed. - 1: Processed, unchanged. */ - int res; - dev_t dev; - ino_t ino; - nlink_t nlink; - unsigned int die_count; -}; - /* 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, - struct file_result *resa, char **files) +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) @@ -15294,64 +15345,6 @@ dwz (const char *file, const char *outfile, struct file_result *res, } res->res = 1; - res->dev = st.st_dev; - res->ino = st.st_ino; - res->nlink = st.st_nlink; - /* Hardlink handling if requested. */ - if (resa != NULL) - { - size_t n; - for (n = 0; &resa[n] != res; n++) - if (resa[n].res >= 0 - && resa[n].nlink > 1 - && resa[n].dev == st.st_dev - && resa[n].ino == st.st_ino) - break; - if (&resa[n] != res) - { - /* 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); - close (fd); - res->res = -2; - return 0; - } - /* 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) - { - close (fd); - res->res = -2; - return 0; - } - unlink (filename); - } - } - } - } - } if (tracing) { @@ -15430,7 +15423,7 @@ dwz (const char *file, const char *outfile, struct file_result *res, + debug_sections[DEBUG_TYPES].new_size)); if (multifile && !fi_multifile && !low_mem) - write_multifile (dso); + write_multifile (dso, res); cleanup (); if (outfile != NULL) @@ -15569,7 +15562,7 @@ dwz (const char *file, const char *outfile, struct file_result *res, debug_sections[DEBUG_GNU_PUBTYPES].new_size = 0; if (multifile && !fi_multifile && !low_mem) - write_multifile (dso); + write_multifile (dso, res); bool save_to_temp = save_temps && multifile && multifile_mode == 0; cleanup (); @@ -15631,15 +15624,17 @@ optimize_multifile (unsigned int *die_count) Elf_Scn *scn; Elf_Data *data; char *e_ident; - const char shstrtab_gnu[] + 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"; - const char shstrtab_dwarf5[] + 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 *shstrtab; - size_t shstrtab_len; + 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; @@ -15665,16 +15660,6 @@ optimize_multifile (unsigned int *die_count) fprintf (stderr, "optimize_multifile\n"); } - if (dwarf_5) - { - shstrtab = shstrtab_dwarf5; - shstrtab_len = sizeof shstrtab_dwarf5; - } - else - { - shstrtab = shstrtab_gnu; - shstrtab_len = sizeof shstrtab_gnu; - } debug_sections[DEBUG_INFO].size = multi_info_off; debug_sections[DEBUG_INFO].data = (multi_info_off @@ -15798,7 +15783,7 @@ optimize_multifile (unsigned int *die_count) hashval_t hash; q = (unsigned char *) strchr ((char *) p, '\0'); - hash = iterative_hash (p, q - 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); @@ -16118,8 +16103,7 @@ read_multifile (int fd, unsigned int die_count) { q = (unsigned char *) strchr ((char *) p, '\0') + 1; slot = htab_find_slot_with_hash (strp_htab, p, - iterative_hash (p, q - p - 1, - 0), INSERT); + hash (p, q - p - 1), INSERT); if (slot == NULL) dwz_oom (); assert (*slot == NULL); @@ -16132,8 +16116,7 @@ read_multifile (int fd, unsigned int die_count) p = debug_sections[DEBUG_STR].data + *pi; q = (unsigned char *) strchr ((char *) p, '\0'); slot = htab_find_slot_with_hash (strp_htab, p, - iterative_hash (p, q - p, - 0), INSERT); + hash (p, q - p), INSERT); if (slot == NULL) dwz_oom (); assert (*slot == NULL); @@ -16157,10 +16140,7 @@ read_multifile (int fd, unsigned int die_count) alt_macro_htab = macro_htab; macro_htab = NULL; alt_first_cu = first_cu; - alt_pool = pool; - pool = NULL; - pool_next = NULL; - pool_limit = NULL; + alt_pool = finalize_pool (); alt_ob = ob; alt_ob2 = ob2; memset (&ob, '\0', sizeof (ob)); @@ -16261,618 +16241,628 @@ make_temp_file (const char *name) return fd; } -int die_count_method_parsed; -int deduplication_mode_parsed; - -/* Options for getopt_long. */ -static struct option dwz_options[] = +/* 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) { - { "help", no_argument, 0, '?' }, - { "output", required_argument, 0, 'o' }, - { "multifile", required_argument, 0, 'm' }, - { "quiet", no_argument, 0, 'q' }, - { "hardlink", no_argument, 0, 'h' }, - { "low-mem-die-limit", required_argument, 0, 'l' }, - { "max-die-limit", required_argument, 0, 'L' }, - { "multifile-name", required_argument, 0, 'M' }, - { "relative", no_argument, 0, 'r' }, - { "version", no_argument, 0, 'v' }, - { "import-optimize", - no_argument, &import_opt_p, 1 }, - { "no-import-optimize", - no_argument, &import_opt_p, 0 }, - { "dwarf-5", no_argument, 0, '5' }, -#if DEVEL - { "devel-trace", no_argument, &tracing, 1 }, - { "devel-progress", no_argument, &progress_p, 1 }, - { "devel-ignore-size", no_argument, &ignore_size, 1 }, - { "devel-ignore-locus",no_argument, &ignore_locus, 1 }, - { "devel-force", no_argument, &force_p, 1 }, - { "devel-save-temps", no_argument, &save_temps, 1 }, - { "devel-dump-checksum", - no_argument, &dump_checksum_p, 1 }, - { "devel-dump-dies", no_argument, &dump_dies_p, 1 }, - { "devel-dump-dups", no_argument, &dump_dups_p, 1 }, - { "devel-dump-pus", no_argument, &dump_pus_p, 1 }, - { "devel-unoptimized-multifile", - no_argument, &unoptimized_multifile, 1 }, - { "devel-verify-edges",no_argument, &verify_edges_p, 1 }, - { "devel-verify-dups", no_argument, &verify_dups_p, 1 }, - { "devel-dump-edges", no_argument, &dump_edges_p, 1 }, - { "devel-partition-dups-opt", - no_argument, &partition_dups_opt, 1 }, - { "devel-die-count-method", - required_argument, &die_count_method_parsed, 1 }, - { "devel-stats", no_argument, &stats_p, 1 }, - { "devel-deduplication-mode", - required_argument, &deduplication_mode_parsed, 1 }, - { "devel-uni-lang", - no_argument, &uni_lang_p, 1 }, - { "devel-no-uni-lang", - no_argument, &uni_lang_p, 0 }, - { "devel-gen-cu", - no_argument, &gen_cu_p, 1 }, - { "devel-no-gen-cu", - no_argument, &gen_cu_p, 0 }, -#endif - { "odr", no_argument, &odr, 1 }, - { "no-odr", no_argument, &odr, 0 }, - { "odr-mode", required_argument, &odr_mode_parsed, 1 }, - { NULL, no_argument, 0, 0 } -}; + int ret; -/* Struct describing various usage aspects of a command line option. */ -struct option_help -{ - const char *short_name; - const char *long_name; - const char *argument; - const char *default_value; - const char *msg; -}; + res->low_mem_p = false; -/* Describe common command line options. */ -static struct option_help dwz_common_options_help[] = -{ - { "q", "quiet", NULL, NULL, - "Silence up the most common messages." }, - { "l", "low-mem-die-limit", "", "10 million DIEs", - "Handle files larger than this limit using a slower and more memory" - " usage friendly mode and don't optimize those files in multifile mode." }, - { "L", "max-die-limit", "", "50 million DIEs", - "Don't optimize files larger than this limit." }, - { NULL, "odr", NULL, NULL, - NULL }, - { NULL, "no-odr", NULL, "Disabled", - "Enable/disable one definition rule optimization." }, - { NULL, "odr-mode", "", "link", - "Set aggressiveness level of one definition rule optimization." }, - { NULL, "import-optimize", NULL, NULL, - NULL }, - { NULL, "no-import-optimize", NULL, "Enabled", - "Enable/disable optimization that reduces the number of" - " DW_TAG_imported_unit DIEs." } -}; + ret = (low_mem_die_limit == 0 + ? 2 + : dwz (file, outfile, res)); -/* Describe single-file command line options. */ -static struct option_help dwz_single_file_options_help[] = -{ - { "o", "output", "OUTFILE", NULL, - "Place the output in OUTFILE." } -}; + if (ret == 2) + { + multifile_mode = MULTIFILE_MODE_LOW_MEM; + res->low_mem_p = true; -/* Describe mult-file command line options. */ -static struct option_help dwz_multi_file_options_help[] = -{ - { "h", "hardlink", NULL, NULL, - "Handle hardlinked files as one file." }, - { "m", "multifile", "COMMONFILE", NULL, - "Enable multifile optimization, placing common DIEs in multifile" - " COMMONFILE." }, - { "M", "multifile-name", "NAME", NULL, - "Set .gnu_debugaltlink or .debug_sup in files to NAME." }, - { "r", "relative", NULL, NULL, - "Set .gnu_debugaltlink in files to relative path from file directory" - " to multifile." }, - { "5", "dwarf-5", NULL, NULL, - "Emit DWARF 5 standardized supplementary object files instead of" - " GNU extension .debug_altlink." } -}; + ret = dwz (file, outfile, res); + } -/* Describe misc command line options. */ -static struct option_help dwz_misc_options_help[] = -{ - { "v", "version", NULL, NULL, - "Display dwz version information." }, - { "?", "help", NULL, NULL, - "Display this information." } -}; - -/* Print LEN spaces to STREAM. */ -static void -do_indent (FILE *stream, unsigned int len) -{ - unsigned int i; - - for (i = 0; i < len; i++) - fprintf (stream, " "); + return ret; } -/* Print MSG to STREAM, indenting to INDENT and wrapping at LIMIT. - Assume starting position is at INDENT. */ +/* Initialize struct file_result RES. */ static void -wrap (FILE *stream, unsigned int indent, unsigned int limit, const char *msg) +init_file_result (struct file_result *res) { - unsigned int len = indent; - const char *s = msg; - while (true) + 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++) { - const char *e = strchr (s, ' '); - unsigned int word_len; - if (e == NULL) - word_len = strlen (s); + 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 - word_len = e - s; - if (word_len == 0) - return; - - if (len + 1 /* space */ + word_len > limit) { - fprintf (stream, "\n"); - do_indent (stream ,indent); - len = indent; - } - else if (len > indent) - { - fprintf (stream, " "); - len += 1; + res->res = 1; + hl_stat[i].dev = st.st_dev; + hl_stat[i].ino = st.st_ino; + hl_stat[i].nlink = st.st_nlink; } - if (e != NULL) + 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) { - const char *i; - for (i = s; i < e; ++i) - fprintf (stream, "%c", *i); + if (tracing) + fprintf (stderr, "Skipping hardlink %s to unchanged file\n", + file); + continue; } - else - fprintf (stream, "%s", s); - len += word_len; - if (e == NULL) - break; - - s = e + 1; + /* 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); + } + } + } } } -/* Print OPTIONS_HELP of length H to STREAM, indenting to help message to - INDENT an wrapping at LIMIT. */ -static void -print_options_help (FILE *stream, struct option_help *options_help, unsigned int n, - unsigned int indent, unsigned int limit) +/* Encode child process exit status. */ +static int +encode_child_exit_status (int thisret, struct file_result *res) { - unsigned len; - const char *s; - unsigned int i; + 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)); +} - for (i = 0; i < n; ++i) +/* 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) { - len = 0; + ret = 0; + res->low_mem_p = true; + } - fprintf (stream, " "); - len += 2; + res->res = (int)(status & 0x7) - 3; + status >>= 3; - s = options_help[i].short_name; - if (s) + 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) { - fprintf (stream, "-%s", s); - len += 2; + error (0, 0, "Could not create multifile temporary files"); + multifile = NULL; } + } - s = options_help[i].long_name; - if (len == 4) - { - fprintf (stream, ", "); - len += 2; - } - fprintf (stream, "--%s", s); - len += 2 + strlen (s); + if (hardlink) + hardlink = detect_hardlinks (nr_files, files, resa); - s = options_help[i].argument; - if (s) - { - fprintf (stream, " %s", s); - len += 1 + strlen (s); - } + 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++; + } - s = options_help[i].msg; - if (s) + 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++) { - if (len > indent) + int i = workset[j]; + + if (nr_forks == max_forks) { - fprintf (stream, "\n"); - do_indent (stream, indent); + 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 - do_indent (stream, indent - len); - len = indent; - - wrap (stream, indent, limit, s); + { + 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++; + } } - fprintf (stream, "\n"); - - s = options_help[i].default_value; - if (s) + if (nr_forks > 0) + wait_children_exit (pids, nr_files, resa); + } + else + { + for (j = 0; j < workset_size; j++) { - do_indent (stream, indent); - fprintf (stream, "Default value: %s.\n", s); + 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; } -/* Print usage and exit. */ -static void -usage (const char *progname, int failing) +/* 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) { - unsigned int n; - unsigned int indent, limit; - FILE *stream = failing ? stderr : stdout; + 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"); - fprintf (stream, - ("Usage:\n" - " %s [common options] [-h] [-m COMMONFILE] [-M NAME | -r] [FILES]\n" - " %s [common options] -o OUTFILE FILE\n" - " %s [ -v | -? ]\n"), - progname, progname, progname); + ret = dwz_files_1 (nr_files, files, hardlink, resa); - indent = 30; - limit = 80; - fprintf (stream, "Common options:\n"); - n = (sizeof (dwz_common_options_help) - / sizeof (dwz_common_options_help[0])); - print_options_help (stream, dwz_common_options_help, n, indent, limit); - - fprintf (stream, "Single-file options:\n"); - n = (sizeof (dwz_single_file_options_help) - / sizeof (dwz_single_file_options_help[0])); - print_options_help (stream, dwz_single_file_options_help, n, indent, limit); - - fprintf (stream, "Multi-file options:\n"); - n = (sizeof (dwz_multi_file_options_help) - / sizeof (dwz_multi_file_options_help[0])); - print_options_help (stream, dwz_multi_file_options_help, n, indent, limit); - - fprintf (stream, "Miscellaneous options:\n"); - n = (sizeof (dwz_misc_options_help) - / sizeof (dwz_misc_options_help[0])); - print_options_help (stream, dwz_misc_options_help, n, indent, limit); - -#if DEVEL - fprintf (stream, "Development options:\n"); - fprintf (stream, "%s", - (" --devel-trace\n" - " --devel-progress\n" - " --devel-stats\n" - " --devel-ignore-size\n" - " --devel-ignore-locus\n" - " --devel-force\n" - " --devel-save-temps\n" - " --devel-dump-checksum\n" - " --devel-dump-dies\n" - " --devel-dump-dups\n" - " --devel-dump-pus\n" - " --devel-unoptimized-multifile\n" - " --devel-verify-dups\n" - " --devel-verify-edges\n" - " --devel-dump-edges\n" - " --devel-partition-dups-opt\n" - " --devel-die-count-method\n" - " --devel-deduplication-mode={none,intra-cu,inter-cu}\n" - " --devel-uni-lang / --devel-no-uni-lang\n" - " --devel-gen-cu / --devel-no-gen-cu\n")); -#endif - - exit (failing); -} - -/* Print version and exit. */ -static void -version (void) -{ - printf ("dwz version " DWZ_VERSION "\n" - "Copyright (C) " RH_YEARS " Red Hat, Inc.\n" - "Copyright (C) " FSF_YEARS " Free Software Foundation, Inc.\n" - "Copyright (C) " SUSE_YEARS " SUSE LLC.\n" - "This program is free software; you may redistribute it under the terms of\n" - "the GNU General Public License version 3 or (at your option) any later version.\n" - "This program has absolutely no warranty.\n"); - exit (0); + free (resa); + return ret; } int main (int argc, char *argv[]) { - const char *outfile = NULL; - int ret = 0; - int i; - unsigned long l; - char *end; - struct file_result res; - bool hardlink = false; - const char *file; + 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"); - while (1) + outfile = NULL; + hardlink = false; + parse_args (argc, argv, &hardlink, &outfile); + nr_files = argc - optind; + files = &argv[optind]; + + if (nr_files <= 1) { - int option_index = -1; - int c = getopt_long (argc, argv, "m:o:qhl:L:M:r?v5", dwz_options, &option_index); - if (c == -1) - break; - switch (c) - { - default: - case '?': - usage (argv[0], option_index == -1); - break; + const char *file = nr_files == 0 ? "a.out" : files[0]; - case 0: - /* Option handled by getopt_long. */ - if (die_count_method_parsed) - { - die_count_method_parsed = 0; - if (strcmp (optarg, "none") == 0) - { - die_count_method = none; - break; - } - if (strcmp (optarg, "estimate") == 0) - { - die_count_method = estimate; - break; - } - error (1, 0, "invalid argument --devel-die-count-method %s", - optarg); - } - if (deduplication_mode_parsed) - { - deduplication_mode_parsed = 0; - if (strcmp (optarg, "none") == 0) - { - deduplication_mode = dm_none; - break; - } - if (strcmp (optarg, "intra-cu") == 0) - { - deduplication_mode = dm_intra_cu; - break; - } - if (strcmp (optarg, "inter-cu") == 0) - { - deduplication_mode = dm_inter_cu; - break; - } - error (1, 0, "invalid argument --devel-deduplication-mode %s", - optarg); - } - if (odr_mode_parsed) - { - odr_mode_parsed = 0; - if (strcmp (optarg, "basic") == 0) - { - odr_mode = ODR_BASIC; - break; - } - if (strcmp (optarg, "link") == 0) - { - odr_mode = ODR_LINK; - break; - } - error (1, 0, "invalid argument --odr-mode %s", - optarg); - } - break; - - case 'o': - outfile = optarg; - break; - - case 'm': - multifile = optarg; - break; - - case 'q': - quiet = true; - break; - - case 'h': - hardlink = true; - break; - - case 'M': - multifile_name = optarg; - break; - - case 'r': - multifile_relative = true; - break; - - case 'l': - if (strcmp (optarg, "none") == 0) - { - low_mem_die_limit = -1U; - break; - } - l = strtoul (optarg, &end, 0); - if (*end != '\0' || optarg == end || (unsigned int) l != l) - error (1, 0, "invalid argument -l %s", optarg); - low_mem_die_limit = l; - break; - - case 'L': - if (strcmp (optarg, "none") == 0) - { - max_die_limit = -1U; - break; - } - l = strtoul (optarg, &end, 0); - if (*end != '\0' || optarg == end || (unsigned int) l != l) - error (1, 0, "invalid argument -L %s", optarg); - max_die_limit = l; - break; - - case '5': - dwarf_5 = true; - break; - - case 'v': - version (); - break; - } - } - - /* Specifying a low-mem die-limit that is larger than or equal to the - max die-limit has the effect of disabling low-mem mode. Make this - explicit by setting it to the 'none' value. */ - if (low_mem_die_limit != -1U - && low_mem_die_limit >= max_die_limit) - low_mem_die_limit = -1U; - - if (multifile_relative && multifile_name) - error (1, 0, "-M and -r options can't be specified together"); - - if (optind == argc || optind + 1 == argc) - { - file = optind == argc ? "a.out" : argv[optind]; if (multifile != NULL) { error (0, 0, "Too few files for multifile optimization"); multifile = NULL; } - if (stats_p) - init_stats (file); - res.die_count = 0; - ret = (low_mem_die_limit == 0 - ? 2 - : dwz (file, outfile, &res, NULL, NULL)); - if (ret == 2) - { - multifile_mode = MULTIFILE_MODE_LOW_MEM; - ret = dwz (file, outfile, &res, NULL, NULL); - } + + ret = dwz_one_file (file, outfile); } else { - int nr_files = argc - optind; - struct file_result *resa - = (struct file_result *) malloc ((nr_files) * sizeof (*resa)); - bool hardlinks = false; - int successcount = 0; - - for (i = 0; i < nr_files; ++i) - resa[i].die_count = 0; - if (resa == NULL) - error (1, ENOMEM, "failed to allocate result array"); if (outfile != NULL) error (1, 0, "-o option not allowed for multiple files"); - if (multifile) - { - 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; - } - } - for (i = optind; i < argc; i++) - { - int thisret; - file = argv[i]; - if (stats_p) - init_stats (file); - thisret = (low_mem_die_limit == 0 - ? 2 - : dwz (file, NULL, &resa[i - optind], - hardlinks ? resa : NULL, &argv[optind])); - if (thisret == 2) - { - multifile_mode = MULTIFILE_MODE_LOW_MEM; - thisret = dwz (file, NULL, &resa[i - optind], - hardlinks ? resa : NULL, &argv[optind]); - } - else if (thisret == 1) - ret = 1; - else if (resa[i - optind].res >= 0) - successcount++; - if (hardlink - && resa[i - optind].res >= 0 - && resa[i - optind].nlink > 1) - hardlinks = true; - } - if (multifile && successcount < 2) - { - error (0, 0, "Too few files for multifile optimization"); - multifile = NULL; - } - if (multifile - && multi_info_off == 0 && multi_str_off == 0 && multi_macro_off == 0) - { - if (!quiet) - error (0, 0, "No suitable DWARF found for multifile optimization"); - multifile = NULL; - } - if (multifile) - { - 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; - else - { - for (i = optind; i < argc; i++) - { - dw_cu_ref cu; - file = argv[i]; - if (stats_p) - init_stats (file); - multifile_mode = MULTIFILE_MODE_FI; - /* Don't process again files that couldn't - be processed successfully. */ - if (resa[i - optind].res == -1) - continue; - for (cu = alt_first_cu; cu; cu = cu->cu_next) - alt_clear_dups (cu->cu_die); - ret |= dwz (file, NULL, &resa[i - optind], - hardlinks ? resa : NULL, &argv[optind]); - } - elf_end (dso->elf); - close (multi_fd); - free (dso); - } - cleanup (); - strp_htab = alt_strp_htab; - off_htab = alt_off_htab; - dup_htab = alt_dup_htab; - macro_htab = alt_macro_htab; - pool = alt_pool; - ob = alt_ob; - ob2 = alt_ob2; - cleanup (); - } - free (resa); + + ret = dwz_files (nr_files, files, hardlink); } if (stats_p) diff --git a/hashtab.c b/hashtab.c index 41eab30..a9b9d13 100644 --- a/hashtab.c +++ b/hashtab.c @@ -626,142 +626,3 @@ htab_restore (htab, name, restorefn) fclose (f); } #endif - -/* DERIVED FROM: --------------------------------------------------------------------- -lookup2.c, by Bob Jenkins, December 1996, Public Domain. -hash(), hash2(), hash3, and mix() are externally useful functions. -Routines to test the hash are included if SELF_TEST is defined. -You can use this free for any purpose. It has no warranty. --------------------------------------------------------------------- -*/ - -/* --------------------------------------------------------------------- -mix -- mix 3 32-bit values reversibly. -For every delta with one or two bit set, and the deltas of all three - high bits or all three low bits, whether the original value of a,b,c - is almost all zero or is uniformly distributed, -* If mix() is run forward or backward, at least 32 bits in a,b,c - have at least 1/4 probability of changing. -* If mix() is run forward, every bit of c will change between 1/3 and - 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) -mix() was built out of 36 single-cycle latency instructions in a - structure that could supported 2x parallelism, like so: - a -= b; - a -= c; x = (c>>13); - b -= c; a ^= x; - b -= a; x = (a<<8); - c -= a; b ^= x; - c -= b; x = (b>>13); - ... - Unfortunately, superscalar Pentiums and Sparcs can't take advantage - of that parallelism. They've also turned some of those single-cycle - latency instructions into multi-cycle latency instructions. Still, - this is the fastest good hash I could find. There were about 2^^68 - to choose from. I only looked at a billion or so. --------------------------------------------------------------------- -*/ -/* same, but slower, works on systems that might have 8 byte hashval_t's */ -#define mix(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<< 8); \ - c -= a; c -= b; c ^= ((b&0xffffffff)>>13); \ - a -= b; a -= c; a ^= ((c&0xffffffff)>>12); \ - b -= c; b -= a; b = (b ^ (a<<16)) & 0xffffffff; \ - c -= a; c -= b; c = (c ^ (b>> 5)) & 0xffffffff; \ - a -= b; a -= c; a = (a ^ (c>> 3)) & 0xffffffff; \ - b -= c; b -= a; b = (b ^ (a<<10)) & 0xffffffff; \ - c -= a; c -= b; c = (c ^ (b>>15)) & 0xffffffff; \ -} - -/* --------------------------------------------------------------------- -hash() -- hash a variable-length key into a 32-bit value - k : the key (the unaligned variable-length array of bytes) - len : the length of the key, counting by bytes - level : can be any 4-byte value -Returns a 32-bit value. Every bit of the key affects every bit of -the return value. Every 1-bit and 2-bit delta achieves avalanche. -About 36+6len instructions. - -The best hash table sizes are powers of 2. There is no need to do -mod a prime (mod is sooo slow!). If you need less than 32 bits, -use a bitmask. For example, if you need only 10 bits, do - h = (h & hashmask(10)); -In which case, the hash table should have hashsize(10) elements. - -If you are hashing n strings (ub1 **)k, do it like this: - for (i=0, h=0; i= 12) /* aligned */ - { - a += *(hashval_t *)(k+0); - b += *(hashval_t *)(k+4); - c += *(hashval_t *)(k+8); - mix(a,b,c); - k += 12; len -= 12; - } - else /* unaligned */ -#endif - while (len >= 12) - { - a += (k[0] +((hashval_t)k[1]<<8) +((hashval_t)k[2]<<16) +((hashval_t)k[3]<<24)); - b += (k[4] +((hashval_t)k[5]<<8) +((hashval_t)k[6]<<16) +((hashval_t)k[7]<<24)); - c += (k[8] +((hashval_t)k[9]<<8) +((hashval_t)k[10]<<16)+((hashval_t)k[11]<<24)); - mix(a,b,c); - k += 12; len -= 12; - } - - /*------------------------------------- handle the last 11 bytes */ - c += length; - switch(len) /* all the case statements fall through */ - { - case 11: c+=((hashval_t)k[10]<<24); /* fall through */ - case 10: c+=((hashval_t)k[9]<<16); /* fall through */ - case 9 : c+=((hashval_t)k[8]<<8); /* fall through */ - /* the first byte of c is reserved for the length */ - case 8 : b+=((hashval_t)k[7]<<24); /* fall through */ - case 7 : b+=((hashval_t)k[6]<<16); /* fall through */ - case 6 : b+=((hashval_t)k[5]<<8); /* fall through */ - case 5 : b+=k[4]; /* fall through */ - case 4 : a+=((hashval_t)k[3]<<24); /* fall through */ - case 3 : a+=((hashval_t)k[2]<<16); /* fall through */ - case 2 : a+=((hashval_t)k[1]<<8); /* fall through */ - case 1 : a+=k[0]; - /* case 0: nothing left to add */ - } - mix(a,b,c); - /*-------------------------------------------- report the result */ - return c; -} diff --git a/hashtab.h b/hashtab.h index cb3da01..5920abc 100644 --- a/hashtab.h +++ b/hashtab.h @@ -153,11 +153,6 @@ extern void htab_restore (htab_t, const char *, htab_restorefn); #endif -/* An iterative hash function for arbitrary data. */ -extern hashval_t iterative_hash (const void *, size_t, hashval_t); -/* Shorthand for hashing something with an intrinsic size. */ -#define iterative_hash_object(OB,INIT) iterative_hash (&OB, sizeof (OB), INIT) - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/pool.c b/pool.c new file mode 100644 index 0000000..c8c7171 --- /dev/null +++ b/pool.c @@ -0,0 +1,103 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek , 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. */ + +/* Big pool allocator. obstack isn't efficient, because it aligns everything + too much, and allocates too small chunks. All these objects are only freed + together. */ + +#include +#include +#include + +#include "pool.h" + +/* Pointer to the start of the current pool chunk, current first free byte + in the chunk and byte after the end of the current pool chunk. */ + +static unsigned char *pool, *pool_next, *pool_limit; + +extern void dwz_oom (void); + +/* Allocate SIZE bytes with ALIGN bytes alignment from the pool. */ +void * +pool_alloc_1 (unsigned int align, unsigned int size) +{ + void *ret; + if (pool == NULL + || (size_t) (pool_limit - pool_next) < (size_t) align + size) + { + size_t new_size = (size_t) align + size; + unsigned char *new_pool; + new_size += sizeof (void *); + if (new_size < 16384 * 1024 - 64) + new_size = 16384 * 1024 - 64; + new_pool = (unsigned char *) malloc (new_size); + if (new_pool == NULL) + dwz_oom (); + *(unsigned char **) new_pool = pool; + pool_next = new_pool + sizeof (unsigned char *); + pool_limit = new_pool + new_size; + pool = new_pool; + } + pool_next = (unsigned char *) (((uintptr_t) pool_next + align - 1) + & ~(uintptr_t) (align - 1)); + ret = pool_next; + pool_next += size; + return ret; +} + +/* Finalize a pool and return it. */ +unsigned char * +finalize_pool (void) +{ + unsigned char *ret = pool; + pool = NULL; + pool_next = NULL; + pool_limit = NULL; + return ret; +} + +/* Free pool P. */ +static void +pool_destroy_1 (unsigned char *p) +{ + while (p) + { + void *elem = (void *) p; + p = *(unsigned char **) p; + free (elem); + } +} + +/* Free pool P, or the current pool if NULL. */ +void +pool_destroy (unsigned char *p) +{ + if (p != NULL) + { + pool_destroy_1 (p); + return; + } + + pool_destroy_1 (pool); + pool = NULL; + pool_next = NULL; + pool_limit = NULL; +} diff --git a/pool.h b/pool.h new file mode 100644 index 0000000..b32d2bf --- /dev/null +++ b/pool.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek , 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. */ + +extern void *pool_alloc_1 (unsigned int, unsigned int); +extern unsigned char *finalize_pool (void); +extern void pool_destroy (unsigned char *); + +#define pool_alloc(name, size) \ + (struct name *) pool_alloc_1 (ALIGNOF_STRUCT (name), size) diff --git a/testsuite/dwz.tests/cycle.c b/testsuite/dwz.tests/cycle.c new file mode 100644 index 0000000..4795a79 --- /dev/null +++ b/testsuite/dwz.tests/cycle.c @@ -0,0 +1,13 @@ +struct s; + +struct s { + struct s *p; +}; + +struct s var; + +int +main (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/cycle.sh b/testsuite/dwz.tests/cycle.sh new file mode 100644 index 0000000..ce42521 --- /dev/null +++ b/testsuite/dwz.tests/cycle.sh @@ -0,0 +1,36 @@ +readelf_flags="" +if readelf -h 2>&1 | grep -q "\-wN"; then + readelf_flags=-wN +fi + +cp $execs/cycle 1 + +# Using mode 3 in checksum_die_ref. +$execs/dwz-for-test 1 -o 1.z --devel-dump-dies 2> DUMP.1 +rm -f 1.z + +# Skipping mode 3 in checksum_die_ref. +$execs/dwz-for-test 1 -o 1.z --devel-dump-dies --devel-no-checksum-cycle-opt 2> DUMP.2 +rm -f 1.z + +# Verify that mode 3 and mode 4 have different checksums. +grep " s structure_type" DUMP.1 > LINE.1 +grep " s structure_type" DUMP.2 > LINE.2 +! diff -q LINE.1 LINE.2 +rm -f DUMP.1 DUMP.2 LINE.1 LINE.2 + +# Verify that dwz actually works with --devel-no-checksum-cycle-opt. +cp 1 2 +$execs/dwz-for-test -m 3 1 2 --devel-no-checksum-cycle-opt --devel-ignore-size + +cnt=$(readelf -wi 3 | grep -c "DW_AT_name.*: s$") +[ $cnt -eq 1 ] + +# Even with -wN readelf 2.38-15.fc37 follows and prints the contents +# of the alt file. So make sure it cannot do that by removing it. +rm 3 + +cnt=$(readelf -wi $readelf_flags 1 | grep -c "DW_AT_name.*: s$" || true) +[ $cnt -eq 0 ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/devel-ignore-locus.sh b/testsuite/dwz.tests/devel-ignore-locus.sh index caa9d78..a8b2dcd 100644 --- a/testsuite/dwz.tests/devel-ignore-locus.sh +++ b/testsuite/dwz.tests/devel-ignore-locus.sh @@ -6,7 +6,7 @@ cnt=$(readelf -wi 1 \ [ $cnt -eq 2 ] - $execs/dwz-for-test 1 2>/dev/null +$execs/dwz-for-test 1 2>/dev/null cnt=$(readelf -wi 1 \ | grep 'DW_AT_name.*: aaa' \ @@ -16,7 +16,7 @@ cnt=$(readelf -wi 1 \ cp $execs/two-typedef 1 - $execs/dwz-for-test --devel-ignore-locus --devel-ignore-size 1 +$execs/dwz-for-test --devel-ignore-locus --devel-ignore-size 1 cnt=$(readelf -wi 1 \ | grep 'DW_AT_name.*: aaa' \ diff --git a/testsuite/dwz.tests/devel-ignore-size.sh b/testsuite/dwz.tests/devel-ignore-size.sh index 78d28a2..a30684f 100644 --- a/testsuite/dwz.tests/devel-ignore-size.sh +++ b/testsuite/dwz.tests/devel-ignore-size.sh @@ -6,7 +6,7 @@ cnt=$(readelf -wi 1 \ [ $cnt -eq 0 ] - $execs/dwz-for-test 1 2>/dev/null +$execs/dwz-for-test 1 2>/dev/null cnt=$(readelf -wi 1 \ | grep '(DW_TAG_partial_unit' \ @@ -18,7 +18,7 @@ fi cp $execs/min 1 - $execs/dwz-for-test --devel-ignore-size 1 +$execs/dwz-for-test --devel-ignore-size 1 cnt=$(readelf -wi 1 \ | grep '(DW_TAG_partial_unit' \ diff --git a/testsuite/dwz.tests/devel-trace.sh b/testsuite/dwz.tests/devel-trace.sh index 1acea4e..50c4a3d 100644 --- a/testsuite/dwz.tests/devel-trace.sh +++ b/testsuite/dwz.tests/devel-trace.sh @@ -1,5 +1,5 @@ cp $execs/hello 1 - $execs/dwz-for-test --devel-trace 1 2>/dev/null +$execs/dwz-for-test --devel-trace 1 2>/dev/null rm -f 1 diff --git a/testsuite/dwz.tests/dw2-skip-prologue.S b/testsuite/dwz.tests/dw2-skip-prologue.S index f249ac0..4d2e198 100644 --- a/testsuite/dwz.tests/dw2-skip-prologue.S +++ b/testsuite/dwz.tests/dw2-skip-prologue.S @@ -400,3 +400,4 @@ loclist: .byte 1 .Lline1_end: + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/dwz.tests/gdb-add-index.sh b/testsuite/dwz.tests/gdb-add-index.sh index 5a91b23..3095efb 100644 --- a/testsuite/dwz.tests/gdb-add-index.sh +++ b/testsuite/dwz.tests/gdb-add-index.sh @@ -1,6 +1,8 @@ cp $execs/hello 1 -gdb-add-index 1 +# Redirect gdb-add-index stderr to stdout. +# https://sourceware.org/bugzilla/show_bug.cgi?id=29316 +gdb-add-index 1 2>&1 readelf -S 1 | grep -q '\.gdb_index' diff --git a/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S b/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S index 7c41f5b..08e1563 100644 --- a/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S +++ b/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S @@ -154,3 +154,4 @@ .byte 0x0 /* Terminator */ .byte 0x0 /* Terminator */ .byte 0x0 /* Terminator */ + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/dwz.tests/odr-struct-multifile.sh b/testsuite/dwz.tests/odr-struct-multifile.sh old mode 100644 new mode 100755 index cc462c9..5961abf --- a/testsuite/dwz.tests/odr-struct-multifile.sh +++ b/testsuite/dwz.tests/odr-struct-multifile.sh @@ -2,6 +2,11 @@ if ! $execs/dwz-for-test --odr -v 2>/dev/null; then exit 77 fi +readelf_flags="" +if readelf -h 2>&1 | grep -q "\-wN"; then + readelf_flags=-wN +fi + cp $execs/odr-struct 1 cp 1 2 @@ -39,14 +44,17 @@ for name in member_one member_two member_three member_four; do [ $cnt -eq 1 ] done +# Even with -wN readelf 2.38-15.fc37 follows and prints the contents +# of the alt file. So make sure it cannot do that by removing it. +rm 3 for name in aaa bbb ccc; do - cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + cnt=$(readelf -wi $readelf_flags 1 | grep -c "DW_AT_name.*:.*$name" || true) [ $cnt -eq 0 ] done for name in member_one member_two member_three member_four; do - cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + cnt=$(readelf -wi $readelf_flags 1 | grep -c "DW_AT_name.*:.*$name" || true) [ $cnt -eq 0 ] done diff --git a/testsuite/dwz.tests/odr-struct-ns.sh b/testsuite/dwz.tests/odr-struct-ns.sh index 4fd00af..c6e0f1d 100644 --- a/testsuite/dwz.tests/odr-struct-ns.sh +++ b/testsuite/dwz.tests/odr-struct-ns.sh @@ -21,7 +21,10 @@ for name in member_one member_two member_three member_four; do esac done -decl_cnt=$(readelf -wi 1 | grep -c "DW_AT_declaration" || true) +# Check that bbb and ccc are present as DW_AT_declaration +readelf -wi 1 | grep -3 DW_AT_declaration > decls +grep bbb decls >/dev/null +grep ccc decls >/dev/null $execs/dwz-for-test --odr 1 @@ -38,8 +41,9 @@ for name in member_one member_two member_three member_four; do done # We expect two decls to be removed, for bbb and ccc. -expected_decl_cnt=$(($decl_cnt - 2)) -decl_cnt=$(readelf -wi 1 | grep -c "DW_AT_declaration" || true) -[ $expected_decl_cnt -eq $decl_cnt ] +readelf -wi 1 | grep -3 DW_AT_declaration > decls || true -rm -f 1 +if grep bbb decls >/dev/null ; then exit 1; fi +if grep ccc decls >/dev/null ; then exit 2; fi + +rm -f 1 decls diff --git a/testsuite/dwz.tests/odr-struct.sh b/testsuite/dwz.tests/odr-struct.sh index d0fddf9..c6793f9 100644 --- a/testsuite/dwz.tests/odr-struct.sh +++ b/testsuite/dwz.tests/odr-struct.sh @@ -21,7 +21,10 @@ for name in member_one member_two member_three member_four; do esac done -decl_cnt=$(readelf -wi 1 | grep -c "DW_AT_declaration" || true) +# Check that bbb and ccc are present as DW_AT_declaration +readelf -wi 1 | grep -3 DW_AT_declaration > decls +grep bbb decls >/dev/null +grep ccc decls >/dev/null $execs/dwz-for-test --odr 1 @@ -38,8 +41,9 @@ for name in member_one member_two member_three member_four; do done # We expect two decls to be removed, for bbb and ccc. -expected_decl_cnt=$(($decl_cnt - 2)) -decl_cnt=$(readelf -wi 1 | grep -c "DW_AT_declaration" || true) -[ $expected_decl_cnt -eq $decl_cnt ] +readelf -wi 1 | grep -3 DW_AT_declaration > decls || true -rm -f 1 +if grep bbb decls >/dev/null ; then exit 1; fi +if grep ccc decls >/dev/null ; then exit 2; fi + +rm -f 1 decls diff --git a/testsuite/dwz.tests/pr24747.sh b/testsuite/dwz.tests/pr24747.sh index f868dbe..7182f72 100644 --- a/testsuite/dwz.tests/pr24747.sh +++ b/testsuite/dwz.tests/pr24747.sh @@ -4,6 +4,4 @@ cp $exec 1 dwz 1 -smaller-than.sh 1 $exec - rm -f 1 diff --git a/testsuite/dwz.tests/twice-multifile.sh b/testsuite/dwz.tests/twice-multifile.sh index 8a66e25..1ff0166 100644 --- a/testsuite/dwz.tests/twice-multifile.sh +++ b/testsuite/dwz.tests/twice-multifile.sh @@ -24,7 +24,10 @@ if [ $(grep -qv "DWARF compression not beneficial" dwz.err \ exit 1 fi -[ $status -eq 0 ] +if [ $status -ne 0 ]; then + cat dwz.err + exit 1 +fi smaller-than.sh 1 1.saved diff --git a/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh b/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh index 7f1e174..ee31359 100644 --- a/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh +++ b/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh @@ -5,9 +5,10 @@ $execs/dwz-for-test \ -l0 \ --devel-trace \ 1 2 \ + -j 1 \ 2> dwz.err -if egrep -q "Compressing (1|2)$" dwz.err; then +if grep -Eq "Compressing (1|2)$" dwz.err; then exit 1 fi diff --git a/testsuite/dwz.tests/varval.S b/testsuite/dwz.tests/varval.S index 126768c..09f65d9 100644 --- a/testsuite/dwz.tests/varval.S +++ b/testsuite/dwz.tests/varval.S @@ -512,3 +512,4 @@ .byte 0x0 /* Terminator */ .byte 0x0 /* Terminator */ .byte 0x0 /* Terminator */ + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/lib/dwarf.exp b/testsuite/lib/dwarf.exp index 8fc6da6..4dbbc6c 100644 --- a/testsuite/lib/dwarf.exp +++ b/testsuite/lib/dwarf.exp @@ -1666,6 +1666,8 @@ namespace eval Dwarf { _write_deferred_output + _section .note.GNU-stack "" progbits + catch {close $_output_file} set _output_file {} } diff --git a/testsuite/scripts/smaller-than.sh b/testsuite/scripts/smaller-than.sh index b3672a5..3b452dc 100755 --- a/testsuite/scripts/smaller-than.sh +++ b/testsuite/scripts/smaller-than.sh @@ -1,10 +1,45 @@ -#!/bin/sh +#!/bin/bash f1=$1 f2=$2 -s1=$(ls -l $f1 | awk '{print $5}') -s2=$(ls -l $f2 | awk '{print $5}') +section_size () +{ + local f="$1" + local section="$2" + + local s + s=$(readelf -S -W $f \ + | grep "\.debug_$section" \ + | sed 's/.*\.debug_//' \ + | awk '{print $5}') + + if [ "$s" = "" ]; then + echo 0 + return + fi + + # Convert hex to decimal. + s=$(printf "%d" $((16#$s))) + + echo $s +} + +size () +{ + local f="$1" + + local total=0 + local section + for section in info abbrev str macro types; do + total=$(($total + $(section_size $f $section))) + done + + echo $total +} + +s1=$(size $f1) +s2=$(size $f2) if [ $s1 -ge $s2 ]; then exit 1 diff --git a/util.h b/util.h new file mode 100644 index 0000000..d542942 --- /dev/null +++ b/util.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek , 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. */ + +/* Utility macros. */ + +#define IMPLIES(A, B) (!((A) && !(B))) + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + +#define XSTR(s) STR(s) +#define STR(s) #s + +#ifndef USE_GNUC +#ifdef __GNUC__ +#define USE_GNUC 1 +#else +#define USE_GNUC 0 +#endif +#endif + +#if USE_GNUC && __GNUC__ >= 3 +# define likely(x) __builtin_expect (!!(x), 1) +# define unlikely(x) __builtin_expect (!!(x), 0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + +#if USE_GNUC +# define FORCE_INLINE __attribute__((always_inline)) +# define UNUSED __attribute__((unused)) +# define USED __attribute__((used)) +#else +# define FORCE_INLINE +# define UNUSED +# define USED +#endif + +#if USE_GNUC +# define ALIGN_STRUCT(name) +# define ALIGNOF_STRUCT(name) __alignof__ (struct name) +#else +# define ALIGN_STRUCT(name) struct align_##name { char c; struct name s; }; +# define ALIGNOF_STRUCT(name) offsetof (struct align_##name, s) +#endif