]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Add dwarflint/ from origin/dwarf branch
authorDjordje Todorovic <djordje.todorovic@rt-rk.com>
Tue, 26 Sep 2017 12:21:47 +0000 (14:21 +0200)
committerMark Wielaard <mark@klomp.org>
Fri, 6 Oct 2017 12:41:37 +0000 (14:41 +0200)
Signed-off-by: Djordje Todorovic <djordje.todorovic@rt-rk.com>
159 files changed:
dwarflint/Makefile.am [new file with mode: 0644]
dwarflint/TODO [new file with mode: 0644]
dwarflint/addr-record.cc [new file with mode: 0644]
dwarflint/addr-record.hh [new file with mode: 0644]
dwarflint/all-dies-it.hh [new file with mode: 0644]
dwarflint/check_debug_abbrev.cc [new file with mode: 0644]
dwarflint/check_debug_abbrev.hh [new file with mode: 0644]
dwarflint/check_debug_abbrev_i.hh [new file with mode: 0644]
dwarflint/check_debug_aranges.cc [new file with mode: 0644]
dwarflint/check_debug_aranges.hh [new file with mode: 0644]
dwarflint/check_debug_aranges_i.hh [new file with mode: 0644]
dwarflint/check_debug_info.cc [new file with mode: 0644]
dwarflint/check_debug_info.hh [new file with mode: 0644]
dwarflint/check_debug_info_i.hh [new file with mode: 0644]
dwarflint/check_debug_line.cc [new file with mode: 0644]
dwarflint/check_debug_line.hh [new file with mode: 0644]
dwarflint/check_debug_line_i.hh [new file with mode: 0644]
dwarflint/check_debug_loc_range.cc [new file with mode: 0644]
dwarflint/check_debug_loc_range.hh [new file with mode: 0644]
dwarflint/check_debug_loc_range_i.hh [new file with mode: 0644]
dwarflint/check_debug_pub.cc [new file with mode: 0644]
dwarflint/check_debug_pub.hh [new file with mode: 0644]
dwarflint/check_die_decl_call.cc [new file with mode: 0644]
dwarflint/check_die_line_info.cc [new file with mode: 0644]
dwarflint/check_die_tree.cc [new file with mode: 0644]
dwarflint/check_die_tree.hh [new file with mode: 0644]
dwarflint/check_die_tree_i.hh [new file with mode: 0644]
dwarflint/check_duplicate_DW_tag_variable.cc [new file with mode: 0644]
dwarflint/check_dups_abstract_origin.cc [new file with mode: 0644]
dwarflint/check_expected_trees.cc [new file with mode: 0644]
dwarflint/check_linkage_external_die.cc [new file with mode: 0644]
dwarflint/check_matching_ranges.cc [new file with mode: 0644]
dwarflint/check_nodebug.cc [new file with mode: 0644]
dwarflint/check_range_out_of_scope.cc [new file with mode: 0644]
dwarflint/check_registrar.hh [new file with mode: 0644]
dwarflint/check_registrar_i.hh [new file with mode: 0644]
dwarflint/check_self_referential_die.cc [new file with mode: 0644]
dwarflint/checkdescriptor.cc [new file with mode: 0644]
dwarflint/checkdescriptor.hh [new file with mode: 0644]
dwarflint/checkdescriptor_i.hh [new file with mode: 0644]
dwarflint/checked_read.cc [new file with mode: 0644]
dwarflint/checked_read.hh [new file with mode: 0644]
dwarflint/checkrule.cc [new file with mode: 0644]
dwarflint/checkrule.hh [new file with mode: 0644]
dwarflint/checks.hh [new file with mode: 0644]
dwarflint/checks_i.hh [new file with mode: 0644]
dwarflint/coverage.cc [new file with mode: 0644]
dwarflint/coverage.hh [new file with mode: 0644]
dwarflint/cu_coverage.cc [new file with mode: 0644]
dwarflint/cu_coverage.hh [new file with mode: 0644]
dwarflint/cu_coverage_i.hh [new file with mode: 0644]
dwarflint/die_locus.cc [new file with mode: 0644]
dwarflint/die_locus.hh [new file with mode: 0644]
dwarflint/dwarf_2.cc [new file with mode: 0644]
dwarflint/dwarf_2.hh [new file with mode: 0644]
dwarflint/dwarf_3.cc [new file with mode: 0644]
dwarflint/dwarf_3.hh [new file with mode: 0644]
dwarflint/dwarf_4.cc [new file with mode: 0644]
dwarflint/dwarf_4.hh [new file with mode: 0644]
dwarflint/dwarf_gnu.cc [new file with mode: 0644]
dwarflint/dwarf_gnu.hh [new file with mode: 0644]
dwarflint/dwarf_mips.cc [new file with mode: 0644]
dwarflint/dwarf_mips.hh [new file with mode: 0644]
dwarflint/dwarf_version-imp.cc [new file with mode: 0644]
dwarflint/dwarf_version-imp.hh [new file with mode: 0644]
dwarflint/dwarf_version.cc [new file with mode: 0644]
dwarflint/dwarf_version.hh [new file with mode: 0644]
dwarflint/dwarf_version_i.hh [new file with mode: 0644]
dwarflint/dwarflint.cc [new file with mode: 0644]
dwarflint/dwarflint.hh [new file with mode: 0644]
dwarflint/dwarflint_i.hh [new file with mode: 0644]
dwarflint/elf_file.hh [new file with mode: 0644]
dwarflint/elf_file_i.hh [new file with mode: 0644]
dwarflint/expected-at.cc [new file with mode: 0644]
dwarflint/expected.hh [new file with mode: 0644]
dwarflint/files.cc [new file with mode: 0644]
dwarflint/files.hh [new file with mode: 0644]
dwarflint/highlevel_check.cc [new file with mode: 0644]
dwarflint/highlevel_check.hh [new file with mode: 0644]
dwarflint/highlevel_check_i.hh [new file with mode: 0644]
dwarflint/locstats.cc [new file with mode: 0644]
dwarflint/locus.cc [new file with mode: 0644]
dwarflint/locus.hh [new file with mode: 0644]
dwarflint/lowlevel_checks.cc [new file with mode: 0644]
dwarflint/lowlevel_checks.hh [new file with mode: 0644]
dwarflint/main.cc [new file with mode: 0644]
dwarflint/main.hh [new file with mode: 0644]
dwarflint/messages.cc [new file with mode: 0644]
dwarflint/messages.hh [new file with mode: 0644]
dwarflint/misc.cc [new file with mode: 0644]
dwarflint/misc.hh [new file with mode: 0644]
dwarflint/option.cc [new file with mode: 0644]
dwarflint/option.hh [new file with mode: 0644]
dwarflint/option_i.hh [new file with mode: 0644]
dwarflint/pri.cc [new file with mode: 0644]
dwarflint/pri.hh [new file with mode: 0644]
dwarflint/readctx.cc [new file with mode: 0644]
dwarflint/readctx.hh [new file with mode: 0644]
dwarflint/reloc.cc [new file with mode: 0644]
dwarflint/reloc.hh [new file with mode: 0644]
dwarflint/section_id.cc [new file with mode: 0644]
dwarflint/section_id.hh [new file with mode: 0644]
dwarflint/sections.cc [new file with mode: 0644]
dwarflint/sections.hh [new file with mode: 0644]
dwarflint/sections_i.hh [new file with mode: 0644]
dwarflint/tests/DW_AT-later-version.bz2 [new file with mode: 0644]
dwarflint/tests/DW_AT_high_pc-below.bz2 [new file with mode: 0644]
dwarflint/tests/DW_AT_high_pc-relative.bz2 [new file with mode: 0644]
dwarflint/tests/aranges_terminate_early.bz2 [new file with mode: 0755]
dwarflint/tests/check_debug_info_refs-1.bz2 [new file with mode: 0755]
dwarflint/tests/check_debug_info_refs-2.bz2 [new file with mode: 0644]
dwarflint/tests/check_range_out_of_scope-1.bz2 [new file with mode: 0755]
dwarflint/tests/check_self_referential_die.bz2 [new file with mode: 0644]
dwarflint/tests/crc7.ko.debug.bz2 [new file with mode: 0644]
dwarflint/tests/debug_abbrev-duplicate-attribute.bz2 [new file with mode: 0644]
dwarflint/tests/empty-1.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-1.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-10.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-11.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-12.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-2.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-3.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-4.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-5.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-6.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-7.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-8.bz2 [new file with mode: 0644]
dwarflint/tests/garbage-9.bz2 [new file with mode: 0644]
dwarflint/tests/hello.bad-1.bz2 [new file with mode: 0644]
dwarflint/tests/hello.bad-1.s [new file with mode: 0644]
dwarflint/tests/hello.bad-2.bz2 [new file with mode: 0644]
dwarflint/tests/hello.bad-2.s [new file with mode: 0644]
dwarflint/tests/hello.bad-3.bz2 [new file with mode: 0644]
dwarflint/tests/hello.bad-3.s [new file with mode: 0644]
dwarflint/tests/libdl-2.12.so.debug.bz2 [new file with mode: 0644]
dwarflint/tests/location-leaks.bz2 [new file with mode: 0644]
dwarflint/tests/nodebug.bz2 [new file with mode: 0755]
dwarflint/tests/null.o.bz2 [new file with mode: 0644]
dwarflint/tests/run-DW_AT-later-version.sh [new file with mode: 0755]
dwarflint/tests/run-DW_AT_high_pc-below.sh [new file with mode: 0755]
dwarflint/tests/run-DW_AT_high_pc-relative.sh [new file with mode: 0755]
dwarflint/tests/run-aranges_terminate_early.sh [new file with mode: 0755]
dwarflint/tests/run-bad.sh [new file with mode: 0755]
dwarflint/tests/run-check_debug_info_refs.sh [new file with mode: 0755]
dwarflint/tests/run-check_duplicate_DW_tag_variable.sh [new file with mode: 0755]
dwarflint/tests/run-check_range_out_of_scope.sh [new file with mode: 0755]
dwarflint/tests/run-check_self_referential_die.sh [new file with mode: 0755]
dwarflint/tests/run-debug_abbrev-duplicate-attribute.sh [new file with mode: 0755]
dwarflint/tests/run-libdl-2.12.so.debug.sh [new file with mode: 0755]
dwarflint/tests/run-location-leaks.sh [new file with mode: 0755]
dwarflint/tests/run-nodebug.sh [new file with mode: 0755]
dwarflint/tests/run-test-all-dies-it.sh [new file with mode: 0755]
dwarflint/tests/run-upper.sh [new file with mode: 0755]
dwarflint/tests/test-all-dies-it.cc [new file with mode: 0644]
dwarflint/tests/test-coverage.cc [new file with mode: 0644]
dwarflint/tests/test-wrap.cc [new file with mode: 0644]
dwarflint/tests/upper.bz2 [new file with mode: 0755]
dwarflint/wrap.cc [new file with mode: 0644]
dwarflint/wrap.hh [new file with mode: 0644]

diff --git a/dwarflint/Makefile.am b/dwarflint/Makefile.am
new file mode 100644 (file)
index 0000000..e1a9c51
--- /dev/null
@@ -0,0 +1,209 @@
+## Process this file with automake to create Makefile.in
+##
+## Copyright (C) 1996-2011 Red Hat, Inc.
+##
+## This file is part of elfutils.
+##
+## This file is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## elfutils is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+include $(top_srcdir)/config/eu.am
+DEFS += $(YYDEBUG) -DDEBUGPRED=@DEBUGPRED@ \
+       -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\"
+INCLUDES += -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
+           -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \
+           -I$(srcdir)/../libasm
+
+AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw
+
+no_mudflap.os = -fmudflap
+
+bin_PROGRAMS = dwarflint locstats
+noinst_PROGRAMS = tests/test-coverage tests/test-wrap tests/test-all-dies-it
+
+dwarflint_SOURCES = \
+       addr-record.cc addr-record.hh \
+       all-dies-it.hh \
+       check_registrar.hh check_registrar_i.hh \
+       checkdescriptor.cc checkdescriptor.hh checkdescriptor_i.hh \
+       checked_read.cc checked_read.hh \
+       checkrule.cc checkrule.hh \
+       checks.hh checks_i.hh \
+       coverage.cc coverage.hh \
+       cu_coverage.cc cu_coverage.hh cu_coverage_i.hh \
+       die_locus.cc die_locus.hh \
+       dwarf_2.cc dwarf_2.hh \
+       dwarf_3.cc dwarf_3.hh \
+       dwarf_4.cc dwarf_4.hh \
+       dwarf_gnu.cc dwarf_gnu.hh \
+       dwarf_mips.cc dwarf_mips.hh \
+       dwarf_version-imp.cc dwarf_version-imp.hh \
+       dwarf_version.cc dwarf_version.hh dwarf_version_i.hh \
+       dwarflint.cc dwarflint.hh dwarflint_i.hh \
+       elf_file.hh elf_file_i.hh \
+       expected-at.cc expected.hh \
+       files.cc files.hh \
+       highlevel_check.cc highlevel_check.hh highlevel_check_i.hh \
+       locus.cc locus.hh \
+       main.cc \
+       messages.cc messages.hh \
+       misc.cc misc.hh \
+       option.cc option.hh option_i.hh \
+       pri.cc pri.hh \
+       readctx.cc readctx.hh \
+       reloc.cc reloc.hh \
+       section_id.cc section_id.hh \
+       sections.cc sections.hh sections_i.hh \
+       wrap.cc wrap.hh \
+       \
+       check_debug_abbrev.cc check_debug_abbrev.hh check_debug_abbrev_i.hh \
+       check_debug_aranges.cc check_debug_aranges.hh check_debug_aranges_i.hh \
+       check_debug_info.cc check_debug_info.hh check_debug_info_i.hh \
+       check_debug_line.cc check_debug_line.hh check_debug_line_i.hh \
+       check_debug_loc_range.cc check_debug_loc_range.hh check_debug_loc_range_i.hh \
+       check_debug_pub.cc check_debug_pub.hh \
+       check_die_tree.cc check_die_tree.hh check_die_tree_i.hh \
+       check_duplicate_DW_tag_variable.cc \
+       check_dups_abstract_origin.cc \
+       check_expected_trees.cc \
+       check_matching_ranges.cc \
+       check_nodebug.cc \
+       check_range_out_of_scope.cc \
+       check_self_referential_die.cc \
+       check_linkage_external_die.cc \
+       check_die_decl_call.cc \
+       check_die_line_info.cc \
+       lowlevel_checks.cc lowlevel_checks.hh \
+       \
+       ../src/dwarfstrings.c
+
+locstats_SOURCES = \
+       locstats.cc \
+       die_locus.cc die_locus.hh \
+       files.cc files.hh \
+       locus.cc locus.hh \
+       option.cc option.hh option_i.hh \
+       section_id.cc section_id.hh \
+       pri.cc pri.hh
+
+tests_test_coverage_SOURCES = tests/test-coverage.cc coverage.cc pri.cc \
+       ../src/dwarfstrings.c
+
+tests_test_wrap_SOURCES = tests/test-wrap.cc wrap.cc
+tests_test_all_dies_it_SOURCES = tests/test-all-dies-it.cc
+
+EXTRA_TESTS = tests/run-debug_abbrev-duplicate-attribute.sh \
+       tests/run-check_duplicate_DW_tag_variable.sh \
+       tests/run-location-leaks.sh \
+       tests/run-nodebug.sh \
+       tests/run-check_range_out_of_scope.sh \
+       tests/run-check_debug_info_refs.sh \
+       tests/run-aranges_terminate_early.sh \
+       tests/run-libdl-2.12.so.debug.sh \
+       tests/run-test-all-dies-it.sh \
+       tests/run-bad.sh \
+       tests/run-check_self_referential_die.sh \
+       tests/run-DW_AT_high_pc-relative.sh \
+       tests/run-DW_AT_high_pc-below.sh \
+       tests/run-DW_AT-later-version.sh \
+       tests/run-upper.sh
+
+TESTS = $(EXTRA_TESTS) \
+       tests/test-coverage \
+       tests/test-wrap
+
+EXTRA_DIST = $(EXTRA_TESTS) \
+       tests/debug_abbrev-duplicate-attribute.bz2 \
+       tests/crc7.ko.debug.bz2 \
+       tests/location-leaks.bz2 \
+       tests/nodebug.bz2 \
+       tests/check_range_out_of_scope-1.bz2 \
+       tests/check_debug_info_refs-1.bz2 \
+       tests/aranges_terminate_early.bz2
+       tests/libdl-2.12.so.debug.bz2 \
+       tests/hello.bad-1.bz2 \
+       tests/hello.bad-3.bz2 \
+       tests/empty-1.bz2 \
+       tests/garbage-1.bz2 \
+       tests/garbage-2.bz2 \
+       tests/garbage-3.bz2 \
+       tests/garbage-4.bz2 \
+       tests/garbage-5.bz2 \
+       tests/garbage-6.bz2 \
+       tests/garbage-7.bz2 \
+       tests/garbage-8.bz2 \
+       tests/garbage-9.bz2 \
+       tests/garbage-10.bz2 \
+       tests/garbage-11.bz2 \
+       tests/garbage-12.bz2 \
+       tests/check_self_referential_die.bz2 \
+       tests/DW_AT_high_pc-relative.bz2 \
+       tests/DW_AT_high_pc-below.bz2 \
+       tests/DW_AT-later-version.bz2 \
+       tests/upper.bz2
+
+installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir) \
+                             bindir=$(DESTDIR)$(bindir) \
+                             $(top_srcdir)/tests/test-wrapper.sh \
+                             installed $(tests_rpath) \
+                             $(program_transform_name)
+if STANDALONE
+TESTS_ENVIRONMENT = $(installed_TESTS_ENVIRONMENT)
+else !STANDALONE
+TESTS_ENVIRONMENT = $(top_srcdir)/tests/test-wrapper.sh \
+                   ../libdw:../backends:../libelf:../libasm
+
+installcheck-local:
+       $(MAKE) $(AM_MAKEFLAGS) \
+               TESTS_ENVIRONMENT='$(installed_TESTS_ENVIRONMENT)' check-TESTS
+endif !STANDALONE
+
+if BUILD_STATIC
+libasm = ../libasm/libasm.a
+libdw = ../libdw/libdw.a $(zip_LIBS) $(libelf) $(libebl) -ldl
+libelf = ../libelf/libelf.a
+else
+libasm = ../libasm/libasm.so
+libdw = ../libdw/libdw.so
+libelf = ../libelf/libelf.so
+endif
+libebl = ../libebl/libebl.a
+libeu = ../lib/libeu.a
+
+# XXX later the C++ stuff will be in libdw.so directly
+libdwpp = ../libdw/libdwpp.a $(libdw)
+
+dwarflint_LDADD  = $(libebl) $(libelf) $(libdwpp) $(libeu) $(libmudflap) -ldl
+locstats_LDADD  = $(libebl) $(libelf) $(libdwpp) $(libeu) $(libmudflap) -ldl
+tests_test_coverage_LDADD  = $(libebl) $(libelf) $(libdwpp) $(libeu) $(libmudflap) -ldl
+tests_test_all_dies_it_LDADD = $(libdwpp)
+
+installcheck-binPROGRAMS: $(bin_PROGRAMS)
+       bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \
+         case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
+          *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
+         esac; \
+         f=`echo "$$p" | \
+            sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+         for opt in --help --version; do \
+           if LD_LIBRARY_PATH=$(DESTDIR)$(libdir) \
+              $(DESTDIR)$(bindir)/$$f $$opt > c$${pid}_.out 2> c$${pid}_.err \
+                && test -n "`cat c$${pid}_.out`" \
+                && test -z "`cat c$${pid}_.err`"; then :; \
+           else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
+         done; \
+       done; rm -f c$${pid}_.???; exit $$bad
+
+CLEANFILES += *.gconv
+
+MAINTAINERCLEANFILES = 
diff --git a/dwarflint/TODO b/dwarflint/TODO
new file mode 100644 (file)
index 0000000..aa61202
--- /dev/null
@@ -0,0 +1,161 @@
+-*-org-*-
+* Generic DWARF support
+  We now have dwarf_version, form and attribute classes.  These can be
+  (and are) set up to support DWARF 2, 3 and 4 separately, as well as
+  the MIPS extension (in a kinda-sorta way, since I've got no binaries
+  to check this).
+
+  But there's still some code around that depends on per-form/per-attr
+  switches, namely check_debug_info.cc::reloc_target and the
+  reference-checking code in the same file.  Maybe the way to do this
+  is to merge all the cl_*ptr into one class cl_pointer and have
+  dwarflint know that the attribute determines what it points to.
+  (Except that some forms determine the target themselves.)  Then
+  declare at relevant attributes the pointer target (a section_id), if
+  any.  I.e. type the forms more richly.
+
+  So that's about the FORMs and ATs.  But there's more, e.g. DW_OP_
+  support.
+
+* low-level checks
+** DW_TAG_type_unit
+   For each type defined in a compilation unit, a contribution may be
+   made to the .debug_types section of the object file. Each such
+   contribution consists of a type unit header (see Section 7.5.1.2)
+   followed by a DW_TAG_type_unit entry, together with its children.
+
+* high-level checks
+
+** DW_OP_GNU_implicit_pointer
+   http://www.mail-archive.com/elfutils-devel@lists.fedorahosted.org/msg00869.html
+
+** const values vs. addresses
+   http://www.mail-archive.com/elfutils-devel@lists.fedorahosted.org/msg00816.html
+
+** dwarflint --stats
+*** mutability probably needs to take into account DW_OP_call*
+   https://fedorahosted.org/pipermail/elfutils-devel/2010-October/001628.html
+
+** expected trees/attributes
+   This is about the check_expected_trees check.  All attributes are
+   marked optional. In future, we need to go through the standard, or
+   employ some other source of knowledge, and adjust the optionality
+   level.
+
+   Also the approach we are taking now is no good.  It ignores changes
+   in DWARF revisions and doesn't tackle the expected children case at
+   all.  It seems that what we need is some sort of XPath-like
+   approach to matching sub-graphs.  Each time one of the queries
+   triggers, a check would be done for expected "neighborhood" of the
+   node.  Such a query might reach far from the original node,
+   spanning layers of parent/child or die/neighbor relationship.
+
+*** DW_AT_byte_size at DW_TAG_pointer_type
+
+    That's from my conversation with Mark:
+
+<mjw> machatap: I was surprised to see all these DW_TAG_pointer_type and
+      DW_TAG_reference_type having an explicit DW_AT_byte_size
+                                                            [2010-09-06 16:59]
+<mjw> machatap: I see that you added the following note in dwarflint:
+                                                            [2010-09-06 17:00]
+<mjw>     .optional (DW_AT_byte_size) // XXX added to reflect reality
+<mjw> Any idea why reality is like that?
+<machatap> mjw: yeah, the XXX meaning "we need to look into that"
+<machatap> I'm afraid I added it there during the mass checks without also
+          putting it on the wiki or somewhere
+<mjw> OK, so you also think that is strange. good. I might not be crazy after
+      all :) [2010-09-06 17:01]
+<machatap> well, it's certainly not per the standard
+
+** DW_AT_location missing vs. optimized-out check
+   a variable/formal_parameter DIE should have some
+   location/declaration/const_value attr
+   https://fedorahosted.org/pipermail/elfutils-devel/2009-March/000179.html
+
+** DW_FORM_* basic sanity checks on attribute values
+   - cache min/max aggregate size per abbrev
+   - when variable-size, check .debug_info data sanity
+
+   This is taken from wiki where it was put way back from some e-mail.
+   I don't even remember what that means anymore but perhaps that's to
+   checks stuff like -1s encoded as signed 0xffffffff etc.
+
+** .debug_frame/.eh_frame (CFI)
+   This wasn't even started yet.
+
+** .debug_loc opcodes
+*** DW_OP_xderef, DW_OP_xderef_size
+    probably flag as unsupported.
+*** DW_OP_call_{2,4}
+    check the operand matches DIE (put off till later)
+*** DW_OP_call_ref
+    this deals with inter-DSO relationships, dwarflint in its current
+    one-file mode can't handle it. Put off till later
+*** DW_OP_nop
+    are these even useful?
+
+* messages
+
+** streams vs fmtstring
+   We now use C++ streams in most places (there are remnants of C
+   printfs in places).  But streams suck for localization.  Personally
+   I'm not too enthusiastic about non-English bugreports, but rest of
+   elfutils is localized, and dwarflint should be too.  I'm afraid
+   that means moving back to some sort of formatting strings.
+
+** filtering
+
+   The current way of filtering is lacking.  We want to filter
+   messages based on:
+   - severity (low, medium, high)
+   - category (e.g. unresolved reference)
+   - area (e.g. .debug_info)
+   - check in question (e.g. check_debug_info)
+   - the message in question (aka I don't want to see this particular
+     message at all)
+
+   What's now there basically works, but it's not configurable from
+   command line, and it's awkward to use.  Plus it stops about halfway
+   through, the bottom part of the stack needs to be implemented via
+   grepping, and that turns our messages into API.
+
+   It seems there are at least two trees of abstractness (area, check
+   and message in question form one, category potentially another,
+   albeit perhaps degenerated) that we may want to filter
+   independently.  E.g. I might want to filter out all
+   .debug_info/sev<1, or I might want to simply filter out sev<1 right
+   away.
+
+* quirks
+
+  Some compilers produce files broken in various ways.  Not to be
+  swamped with irrelevant known-broken stuff, we'd like dwarflint to
+  know about these "quirks" and be able to suppress them.  I expect
+  there to be frequent additions to this "quirks table", so it should
+  be fairly easy to extend this.
+
+  (Maybe quirk is simply a dwarf_version.  After loading the CU DIE,
+  dwarflint would consult the quirk table and construct new
+  dwarf_version with appropriate exceptions.)
+
+  The current option --tolerant will go away when this is implemented.
+  I don't think it even works anyway.
+
+* multi-file mode
+
+  While dwarflint can check several files (meaning you can put several
+  file names to a command line), it treats each of them in isolation.
+  In theory these files can link to each other and form graphs, and
+  dwarflint should be able to check the whole graph.
+
+* failure tolerance
+
+  We'd like dwarflint to do things like checking each CU the abbrev
+  table of which I was able to read.  In fact dwarflint should check
+  even CUs that it has at least partial information about.  It could
+  bail out as soon as it hits invalid abbrev, or abbrev with unknown
+  attribute.  Even then it might give up its goal of validating
+  sibling references, and use them blindly to skip unknown portions.
+  Current check granularity makes this very awkward to express, I'll
+  have to rework how checks are defined and executed.
diff --git a/dwarflint/addr-record.cc b/dwarflint/addr-record.cc
new file mode 100644 (file)
index 0000000..59dfa00
--- /dev/null
@@ -0,0 +1,60 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "addr-record.hh"
+
+size_t
+addr_record::find (uint64_t addr) const
+{
+  size_t a = 0;
+  size_t b = size ();
+
+  while (a < b)
+    {
+      size_t i = (a + b) / 2;
+      uint64_t v = (*this)[i];
+
+      if (v > addr)
+       b = i;
+      else if (v < addr)
+       a = i + 1;
+      else
+       return i;
+    }
+
+  return a;
+}
+
+bool
+addr_record::has_addr (uint64_t addr) const
+{
+  if (begin () == end ()
+      || addr < front ()
+      || addr > back ())
+    return false;
+
+  const_iterator it = begin () + find (addr);
+  return it != end () && *it == addr;
+}
+
+void
+addr_record::add (uint64_t addr)
+{
+  iterator it = begin () + find (addr);
+  if (it == end () || *it != addr)
+    insert (it, addr);
+}
diff --git a/dwarflint/addr-record.hh b/dwarflint/addr-record.hh
new file mode 100644 (file)
index 0000000..a717d23
--- /dev/null
@@ -0,0 +1,74 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_ADDR_RECORD_H
+#define DWARFLINT_ADDR_RECORD_H
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <vector>
+
+#include "locus.hh"
+
+/// Address record is used to check that all DIE references actually
+/// point to an existing die, not somewhere mid-DIE, where it just
+/// happens to be interpretable as a DIE.  This is stored as sorted
+/// array for quick lookup and duplicate removal.
+struct addr_record
+  : private std::vector<uint64_t>
+{
+  typedef std::vector<uint64_t> _super_t;
+  size_t find (uint64_t addr) const;
+
+public:
+  bool has_addr (uint64_t addr) const;
+  void add (uint64_t addr);
+};
+
+/// One reference for use in ref_record, parametrized by locus type.
+template <class L>
+struct ref_T
+{
+  uint64_t addr; // Referee address
+  L who;         // Referrer
+
+  ref_T ()
+    : addr (-1)
+  {}
+
+  ref_T (uint64_t a_addr, L const &a_who)
+    : addr (a_addr)
+    , who (a_who)
+  {}
+};
+
+/// Reference record is used to check validity of DIE references.
+/// Unlike the above, this is not stored as sorted set, but simply as
+/// an array of records, because duplicates are unlikely.
+template <class L>
+class ref_record_T
+  : private std::vector<ref_T<L> >
+{
+  typedef std::vector<ref_T<L> > _super_t;
+public:
+  using _super_t::const_iterator;
+  using _super_t::begin;
+  using _super_t::end;
+  using _super_t::push_back;
+};
+
+#endif//DWARFLINT_ADDR_RECORD_H
diff --git a/dwarflint/all-dies-it.hh b/dwarflint/all-dies-it.hh
new file mode 100644 (file)
index 0000000..fe30057
--- /dev/null
@@ -0,0 +1,142 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <vector>
+#include <stdexcept>
+
+// Tree flattening iterator.  It pre-order iterates all CUs in given
+// dwarf file.
+template <class T>
+class all_dies_iterator
+  : public std::iterator<std::input_iterator_tag,
+                        typename T::debug_info_entry>
+{
+  typedef typename T::debug_info_entry::children_type::const_iterator die_it_t;
+  typedef std::vector <std::pair <die_it_t, die_it_t> > die_it_stack_t;
+
+  typename T::compile_units_type::const_iterator _m_cu_it, _m_cu_it_end;
+  die_it_t _m_die_it, _m_die_it_end;
+  die_it_stack_t _m_die_it_stack;
+  bool _m_atend;
+
+  void start ()
+  {
+    if (_m_cu_it == _m_cu_it_end)
+      _m_atend = true;
+    else
+      {
+       _m_die_it = die_it_t (*_m_cu_it);
+       _m_die_it_end = die_it_t ();
+       ++_m_cu_it;
+       assert (_m_die_it != _m_die_it_end);
+      }
+  }
+
+public:
+  // An end iterator.
+  all_dies_iterator ()
+    : _m_atend (true)
+  {}
+
+  explicit all_dies_iterator (T const &dw)
+    : _m_cu_it (dw.compile_units ().begin ())
+    , _m_cu_it_end (dw.compile_units ().end ())
+    , _m_atend (_m_cu_it == _m_cu_it_end)
+  {
+    if (!_m_atend)
+      start ();
+  }
+
+  bool operator== (all_dies_iterator const &other)
+  {
+    return (_m_atend && other._m_atend)
+      || (_m_cu_it == other._m_cu_it
+         && _m_die_it == other._m_die_it
+         && _m_die_it_stack == other._m_die_it_stack);
+  }
+
+  bool operator!= (all_dies_iterator const &other)
+  {
+    return !(*this == other);
+  }
+
+  all_dies_iterator operator++ () // prefix
+  {
+    if (!_m_atend)
+      {
+       if (_m_die_it->has_children ()
+           && _m_die_it->children ().begin () != _m_die_it->children ().end ())
+         {
+           _m_die_it_stack.push_back (std::make_pair (_m_die_it, _m_die_it_end));
+           _m_die_it_end = _m_die_it->children ().end ();
+           _m_die_it = _m_die_it->children ().begin ();
+         }
+       else
+         while (++_m_die_it == _m_die_it_end)
+
+           {
+             if (_m_die_it_stack.size () == 0)
+               {
+                 start ();
+                 break;
+               }
+             _m_die_it = _m_die_it_stack.back ().first;
+             _m_die_it_end = _m_die_it_stack.back ().second;
+             _m_die_it_stack.pop_back ();
+           }
+      }
+    return *this;
+  }
+
+  all_dies_iterator operator++ (int) // postfix
+  {
+    all_dies_iterator prev = *this;
+    ++*this;
+    return prev;
+  }
+
+  typename T::debug_info_entry const &operator* () const
+  {
+    if (unlikely (_m_atend))
+      throw std::runtime_error ("dereferencing end iterator");
+    return *_m_die_it;
+  }
+
+  std::vector<typename T::debug_info_entry> stack () const
+  {
+    std::vector<typename T::debug_info_entry> ret;
+    for (auto it = _m_die_it_stack.begin ();
+        it != _m_die_it_stack.end (); ++it)
+      ret.push_back (*it->first);
+    ret.push_back (*_m_die_it);
+    return ret;
+  }
+
+  typename T::compile_unit cu () const
+  {
+    return *_m_cu_it;
+  }
+
+  typename T::debug_info_entry const *operator-> () const
+  {
+    return &**this;
+  }
+};
diff --git a/dwarflint/check_debug_abbrev.cc b/dwarflint/check_debug_abbrev.cc
new file mode 100644 (file)
index 0000000..e731323
--- /dev/null
@@ -0,0 +1,574 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+#include <sstream>
+#include <cassert>
+#include <algorithm>
+
+#include "../libdw/c++/dwarf"
+
+#include "check_debug_info.hh"
+#include "check_debug_abbrev.hh"
+#include "pri.hh"
+#include "dwarf_version.hh"
+#include "sections.hh"
+#include "checked_read.hh"
+#include "messages.hh"
+#include "misc.hh"
+
+char const *
+locus_simple_fmt::abbr_offset_n ()
+{
+  return "abbr. offset";
+}
+
+abbrev_attrib_locus::abbrev_attrib_locus (uint64_t abbr_offset,
+                                         uint64_t attr_offset,
+                                         int a_name)
+  : _m_abbr_offset (abbr_offset)
+  , _m_attr_offset (attr_offset)
+  , _m_name (a_name)
+{}
+
+abbrev_attrib_locus::abbrev_attrib_locus (abbrev_attrib_locus const &copy)
+  : _m_abbr_offset (copy._m_abbr_offset)
+  , _m_attr_offset (copy._m_attr_offset)
+  , _m_name (copy._m_name)
+{}
+
+std::string
+abbrev_attrib_locus::name () const
+{
+  return pri::attr_name (_m_name);
+}
+
+void
+abbrev_attrib_locus::set_name (int a_name)
+{
+  _m_name = a_name;
+}
+
+abbrev_attrib_locus
+abbrev_attrib_locus::non_symbolic ()
+{
+  return abbrev_attrib_locus (_m_abbr_offset, _m_attr_offset);
+}
+
+std::string
+abbrev_attrib_locus::format (bool brief) const
+{
+  std::stringstream ss;
+  if (!brief)
+    ss << section_name[sec_abbrev] << ": ";
+  if (_m_name != -1)
+    ss << "abbr. 0x" << std::hex << _m_abbr_offset << ", attr. " << name ();
+  else
+    ss << "abbr. attribute 0x" << std::hex << _m_attr_offset;
+  return ss.str ();
+}
+
+checkdescriptor const *
+check_debug_abbrev::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_abbrev")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"Checks for low-level structure of .debug_abbrev.  In addition it "
+"checks:\n"
+" - that all abbreviation tables are non-empty\n"
+" - that certain attribute forms match expectations (mainly those that "
+"we have to work with in subsequent check passes.  For example we "
+"check that DW_AT_low_pc has a form of DW_FORM_{,ref_}addr)\n"
+" - that all CUs that share an abbrev table are of the same DWARF "
+"version\n"
+" - that each abbrev table is used\n"
+" - that abbrevs don't share abbrev codes\n"
+" - that abbrev tags, attribute names and attribute forms are all known "
+"(note that this assumes that elfutils know about all tags used in "
+"practice.  Be sure to build against recent-enough version)\n"
+" - that the value of has_children is either 0 or 1\n"
+" - that DW_AT_sibling isn't formed as DW_FORM_ref_addr, and that it "
+"isn't present at childless abbrevs\n"
+" - that attributes are not duplicated at abbrev\n"
+" - that DW_AT_high_pc is never used without DW_AT_low_pc.  If both are "
+"used, that DW_AT_ranges isn't also used\n"
+"This check generally requires CU headers to be readable, i.e. that the "
+".debug_info section is roughly well-defined.  If that isn't the case, "
+"many checks will still be done, operating under assumption that what "
+"we see is the latest DWARF format.  This may render some checks "
+"inaccurate.\n"));
+  return &cd;
+}
+
+static reg<check_debug_abbrev> reg_debug_abbrev;
+
+abbrev *
+abbrev_table::find_abbrev (uint64_t abbrev_code) const
+{
+  size_t a = 0;
+  size_t b = size;
+  struct abbrev *ab = NULL;
+
+  while (a < b)
+    {
+      size_t i = (a + b) / 2;
+      ab = abbr + i;
+
+      if (ab->code > abbrev_code)
+       b = i;
+      else if (ab->code < abbrev_code)
+       a = i + 1;
+      else
+       return ab;
+    }
+
+  return NULL;
+}
+
+namespace
+{
+  struct cmp_abbrev
+  {
+    bool operator () (abbrev const &a, abbrev const &b) const
+    {
+      return a.code < b.code;
+    }
+  };
+
+  void
+  complain (locus const &loc, int form_name,
+           bool indirect, char const *qualifier)
+  {
+    wr_error (loc)
+      << "attribute with " << qualifier << (indirect ? " indirect" : "")
+      << " form " << elfutils::dwarf::forms::identifier (form_name)
+      << '.' << std::endl;
+  }
+
+  bool
+  check_no_abbreviations (check_debug_abbrev::abbrev_map const &abbrevs)
+  {
+    bool ret = abbrevs.begin () == abbrevs.end ();
+
+    // It's not an error when the abbrev table contains no abbrevs.
+    // But since we got here, apparently there was a .debug_abbrev
+    // section with size of more than 0 bytes, which is wasteful.
+    if (ret)
+      wr_message (section_locus (sec_abbrev),
+                 mc_abbrevs | mc_impact_1 | mc_acc_bloat)
+       << "no abbreviations." << std::endl;
+    return ret;
+  }
+
+  check_debug_abbrev::abbrev_map
+  load_debug_abbrev (sec &sect, elf_file &file,
+                    read_cu_headers *cu_headers)
+  {
+    check_debug_abbrev::abbrev_map abbrevs;
+
+    read_ctx ctx;
+    read_ctx_init (&ctx, sect.data, file.other_byte_order);
+
+    struct abbrev_table *section = NULL;
+    uint64_t first_attr_off = 0;
+
+    // Tolerate failure here.
+    dwarf_version const *ver = NULL;
+    static dwarf_version const *latest_ver = dwarf_version::get_latest ();
+
+    bool failed = false;
+    while (true)
+      {
+       /* If we get EOF at this point, either the CU was improperly
+          terminated, or there were no data to begin with.  */
+       if (read_ctx_eof (&ctx))
+         {
+           if (!check_no_abbreviations (abbrevs))
+             wr_error (section_locus (sec_abbrev))
+               << "missing zero to mark end-of-table.\n";
+           break;
+         }
+
+       uint64_t abbr_off;
+       uint64_t abbr_code;
+       {
+         uint64_t prev_abbr_code = (uint64_t)-1;
+         uint64_t zero_seq_off = (uint64_t)-1;
+
+         do
+           {
+             abbr_off = read_ctx_get_offset (&ctx);
+
+             /* Abbreviation code.  */
+             if (!checked_read_uleb128 (&ctx, &abbr_code,
+                                        section_locus (sec_abbrev, abbr_off),
+                                        "abbrev code"))
+               throw check_base::failed ();
+
+             /* Note: we generally can't tell the difference between
+                empty table and (excessive) padding.  But NUL byte(s)
+                at the very beginning of section are almost certainly
+                the first case.  */
+             if (zero_seq_off == (uint64_t)-1
+                 && abbr_code == 0
+                 && (prev_abbr_code == 0
+                     || abbrevs.empty ()))
+               zero_seq_off = abbr_off;
+
+             if (abbr_code != 0)
+               break;
+             else
+               section = NULL;
+
+             prev_abbr_code = abbr_code;
+           }
+         while (!read_ctx_eof (&ctx)
+                /* On EOF, shift the offset so that beyond-EOF
+                   end-position is printed for padding warning.
+                   Necessary as our end position is exclusive.  */
+                || ((abbr_off += 1), false));
+
+         if (zero_seq_off != (uint64_t)-1)
+           wr_message_padding_0 (mc_abbrevs | mc_header,
+                                 section_locus (sec_abbrev),
+                                 zero_seq_off, abbr_off);
+       }
+
+       if (read_ctx_eof (&ctx))
+         {
+           /* It still could have been empty.  */
+           check_no_abbreviations (abbrevs);
+           break;
+         }
+
+       abbrev_locus where (abbr_off);
+
+       /* OK, we got some genuine abbreviation.  See if we need to
+          allocate a new section.  */
+       if (section == NULL)
+         {
+           abbrev_table t;
+           section = &abbrevs.insert (std::make_pair (abbr_off, t)).first->second;
+           section->offset = abbr_off;
+
+           // Find CU that uses this abbrev table, so that we know what
+           // version to validate against.
+           if (cu_headers != NULL)
+             {
+               ver = NULL;
+               cu_head const *other_head = NULL;
+               for (std::vector <cu_head>::const_iterator it
+                      = cu_headers->cu_headers.begin ();
+                    it != cu_headers->cu_headers.end (); ++it)
+                 if (it->abbrev_offset == abbr_off)
+                   {
+                     section->used = true;
+                     dwarf_version const *nver
+                       = dwarf_version::get (it->version);
+                     if (ver == NULL)
+                       ver = nver;
+                     else if (nver != ver)
+                       {
+                         wr_error (it->where)
+                           << " and " << other_head->where << " both use "
+                           << where << ", but each has a different version ("
+                           << it->version << " vs. " << other_head->version
+                           << ")." << std::endl;
+
+                         // Arbitrarily pick newer version.
+                         if (it->version > other_head->version)
+                           ver = nver;
+                       }
+
+                     other_head = &*it;
+                   }
+
+               if (ver == NULL)
+                 {
+                   // This is hard error, we can't validate abbrev
+                   // table without knowing what version to use.
+                   wr_error (where)
+                     << "abbreviation table is never used." << std::endl;
+                   ver = dwarf_version::get_latest ();
+                 }
+             }
+           else if (ver == NULL) // Only emit this once.
+             {
+               wr_error (section_locus (sec_info))
+                 << "couldn't load CU headers for processing .debug_abbrev; "
+                    "assuming latest DWARF flavor."
+                 << std::endl;
+               ver = latest_ver;
+             }
+
+           assert (ver != NULL);
+         }
+
+       abbrev *original = section->find_abbrev (abbr_code);
+       abbrev *cur;
+       abbrev fake (where);
+       if (unlikely (original != NULL))
+         {
+           wr_error (where) << "duplicate abbrev code (first was at "
+                            << original->where << ").\n";
+           /* Don't actually save this abbrev if it's duplicate.  */
+           cur = &fake;
+         }
+       else
+         {
+           REALLOC (section, abbr);
+           cur = section->abbr + section->size++;
+           new (cur) abbrev (where);
+         }
+
+       cur->code = abbr_code;
+
+       /* Abbreviation tag.  */
+       uint64_t abbr_tag;
+       if (!checked_read_uleb128 (&ctx, &abbr_tag, where, "abbrev tag"))
+         throw check_base::failed ();
+
+       if (abbr_tag > DW_TAG_hi_user)
+         {
+           wr_error (where)
+             << "invalid abbrev tag " << pri::hex (abbr_tag)
+             << '.' << std::endl;
+           throw check_base::failed ();
+         }
+       cur->tag = (typeof (cur->tag))abbr_tag;
+
+       /* Abbreviation has_children.  */
+       uint8_t has_children;
+       if (!read_ctx_read_ubyte (&ctx, &has_children))
+         {
+           wr_error (&where, ": can't read abbrev has_children.\n");
+           throw check_base::failed ();
+         }
+
+       if (has_children != DW_CHILDREN_no
+           && has_children != DW_CHILDREN_yes)
+         {
+           wr_error (where)
+             << "invalid has_children value " << pri::hex (cur->has_children)
+             << '.' << std::endl;
+           throw check_base::failed ();
+         }
+       cur->has_children = has_children == DW_CHILDREN_yes;
+
+       bool null_attrib;
+       bool low_pc = false;
+       bool high_pc = false;
+       bool ranges = false;
+       std::map<unsigned, uint64_t> seen;
+
+       do
+         {
+           uint64_t attr_off = read_ctx_get_offset (&ctx);
+           uint64_t attrib_name, attrib_form;
+           if (first_attr_off == 0)
+             first_attr_off = attr_off;
+
+           /* Shift to match elfutils reporting.  */
+           attr_off -= first_attr_off;
+           abbrev_attrib_locus attr_locus (abbr_off, attr_off);
+
+           /* Load attribute name and form.  */
+           if (!checked_read_uleb128 (&ctx, &attrib_name, attr_locus,
+                                      "attribute name"))
+             throw check_base::failed ();
+
+           if (!checked_read_uleb128 (&ctx, &attrib_form, attr_locus,
+                                      "attribute form"))
+             throw check_base::failed ();
+
+           /* Now if both are zero, this was the last attribute.  */
+           null_attrib = attrib_name == 0 && attrib_form == 0;
+
+           REALLOC (cur, attribs);
+
+           attr_locus.set_name (attrib_name);
+           struct abbrev_attrib *acur = cur->attribs + cur->size++;
+           new (acur) abbrev_attrib ();
+           acur->name = attrib_name;
+           acur->form = attrib_form;
+           acur->where = attr_locus;
+
+           if (null_attrib)
+             break;
+
+           /* Otherwise validate name and form.  */
+           if (attrib_name == 0)
+             {
+               wr_error (attr_locus.non_symbolic ())
+                 << "invalid attribute code 0." << std::endl;
+               // We can handle this, so keep going.  But this is not
+               // kosher for high-level checks.
+               failed = true;
+               continue;
+             }
+
+           attribute const *attribute = ver->get_attribute (attrib_name);
+           if (attribute == NULL)
+             {
+               // GCC commonly emits DWARF 2 with trivial extensions
+               // (such as attribute names) from newer versions.  In
+               // GNU mode, don't even mind this.  In non-gnu, emit
+               // warning.  We explicitly don't do this for forms,
+               // where the consumer wouldn't know how to read or
+               // skip the datum.
+               attribute = latest_ver->get_attribute (attrib_name);
+               if (attribute == NULL)
+                 // libdw should handle unknown attribute, as long as
+                 // the form is kosher, so don't fail the check.
+                 wr_message (attr_locus.non_symbolic (),
+                             mc_abbrevs | mc_impact_1)
+                   << "invalid or unknown name " << pri::hex (attrib_name)
+                   << '.' << std::endl;
+               else if (opt_nognu)
+                 wr_message (attr_locus, mc_abbrevs | mc_impact_1)
+                   << "attribute from later DWARF version."
+                   << std::endl;
+             }
+
+           form const *form = check_debug_abbrev::check_form
+             (ver, attribute, attrib_form, attr_locus, false);
+           if (form == NULL)
+             {
+               // Error message has been emitted in check_form.
+               failed = true;
+               continue;
+             }
+
+           std::pair<std::map<unsigned, uint64_t>::iterator, bool> inserted
+             = seen.insert (std::make_pair (attrib_name, attr_off));
+           if (!inserted.second)
+             {
+               wr_error (attr_locus.non_symbolic ())
+                 << "duplicate attribute " << attr_locus.name ()
+                 << " (first was at " << pri::hex (inserted.first->second)
+                 << ")." << std::endl;
+
+               // I think we may allow such files for high-level
+               // consumption, so don't fail the check...
+               if (attrib_name == DW_AT_sibling)
+                 // ... unless it's DW_AT_sibling.
+                 failed = true;
+             }
+
+           if (attrib_name == DW_AT_sibling && !cur->has_children)
+             wr_message (attr_locus, mc_die_rel | mc_acc_bloat | mc_impact_1)
+               << "superfluous DW_AT_sibling attribute at childless abbrev."
+               << std::endl;
+           if (attrib_name == DW_AT_ranges)
+             ranges = true;
+           else if (attrib_name == DW_AT_low_pc)
+             low_pc = true;
+           else if (attrib_name == DW_AT_high_pc)
+             high_pc = true;
+         }
+       while (!null_attrib);
+
+       if (high_pc && !low_pc)
+         wr_error (where)
+           << "the abbrev has DW_AT_high_pc without also having DW_AT_low_pc."
+           << std::endl;
+       else if (high_pc && ranges)
+         wr_error (where)
+           << "the abbrev has DW_AT_high_pc & DW_AT_low_pc, "
+           << "but also has DW_AT_ranges." << std::endl;
+      }
+
+    if (failed)
+      throw check_base::failed ();
+
+    abbrev_table *last = NULL;
+    for (check_debug_abbrev::abbrev_map::iterator it = abbrevs.begin ();
+        it != abbrevs.end (); ++it)
+      {
+       std::sort (it->second.abbr, it->second.abbr + it->second.size,
+                  cmp_abbrev ());
+       if (last != NULL)
+         last->next = &it->second;
+       last = &it->second;
+      }
+
+    return abbrevs;
+  }
+}
+
+check_debug_abbrev::check_debug_abbrev (checkstack &stack, dwarflint &lint)
+  : _m_sec_abbr (lint.check (stack, _m_sec_abbr))
+  , _m_cu_headers (lint.toplev_check (stack, _m_cu_headers))
+  , abbrevs (load_debug_abbrev (_m_sec_abbr->sect,
+                               _m_sec_abbr->file,
+                               _m_cu_headers))
+{
+}
+
+form const *
+check_debug_abbrev::check_form (dwarf_version const *ver,
+                               attribute const *attribute,
+                               int form_name, locus const &loc, bool indirect)
+{
+  form const *form = ver->get_form (form_name);
+  if (form == NULL)
+    {
+      wr_error (loc)
+       << "invalid form " << pri::hex (form_name)
+       << '.' << std::endl;
+      return NULL;
+    }
+
+  if (attribute != NULL)
+    {
+
+      int attrib_name = attribute->name ();
+      if (!ver->form_allowed (attribute, form))
+       {
+         complain (loc, form_name, indirect, "invalid");
+         return NULL;
+       }
+      else if (attrib_name == DW_AT_sibling
+              && sibling_form_suitable (ver, form) == sfs_long)
+       complain (loc, form_name, indirect, "unsuitable");
+    }
+
+  return form;
+}
+
+check_debug_abbrev::~check_debug_abbrev ()
+{
+  // xxx So using new[]/delete[] would be nicer (delete ignores
+  // const-ness), but I'm not dipping into that right now.  Just cast
+  // away the const, we're in the dtor so what the heck.
+  abbrev_map &my_abbrevs = const_cast<abbrev_map &> (abbrevs);
+
+  for (abbrev_map::iterator it = my_abbrevs.begin ();
+       it != my_abbrevs.end (); ++it)
+    {
+      for (size_t i = 0; i < it->second.size; ++i)
+       free (it->second.abbr[i].attribs);
+      free (it->second.abbr);
+    }
+}
diff --git a/dwarflint/check_debug_abbrev.hh b/dwarflint/check_debug_abbrev.hh
new file mode 100644 (file)
index 0000000..ef5531a
--- /dev/null
@@ -0,0 +1,144 @@
+/* Low-level checking of .debug_abbrev.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECK_DEBUG_ABBREV_HH
+#define DWARFLINT_CHECK_DEBUG_ABBREV_HH
+
+#include "checks.hh"
+#include "sections_i.hh"
+#include "check_debug_info_i.hh"
+#include "dwarf_version_i.hh"
+
+namespace locus_simple_fmt
+{
+  char const *abbr_offset_n ();
+}
+
+typedef fixed_locus<sec_abbrev,
+                   locus_simple_fmt::abbr_offset_n,
+                   locus_simple_fmt::hex> abbrev_locus;
+
+class abbrev_attrib_locus
+  : public locus
+{
+  uint64_t _m_abbr_offset;
+  uint64_t _m_attr_offset;
+  int _m_name;
+
+public:
+  explicit abbrev_attrib_locus (uint64_t abbr_offset = -1,
+                               uint64_t attr_offset = -1,
+                               int name = -1);
+
+  abbrev_attrib_locus (abbrev_attrib_locus const &copy);
+
+  abbrev_attrib_locus non_symbolic ();
+
+  void set_name (int name);
+  std::string format (bool brief = false) const;
+  std::string name () const;
+};
+
+struct abbrev_attrib
+{
+  abbrev_attrib_locus where;
+  uint16_t name;
+  uint8_t form;
+
+  abbrev_attrib ()
+    : where ()
+    , name (0)
+    , form (0)
+  {}
+};
+
+struct abbrev
+{
+  abbrev_locus where;
+  uint64_t code;
+
+  /* Attributes.  */
+  abbrev_attrib *attribs;
+  size_t size;
+  size_t alloc;
+
+  /* While ULEB128 can hold numbers > 32bit, these are not legal
+     values of many enum types.  So just use as large type as
+     necessary to cover valid values.  */
+  uint16_t tag;
+  bool has_children;
+
+  /* Whether some DIE uses this abbrev.  */
+  bool used;
+
+  explicit abbrev (abbrev_locus const &loc)
+    : where (loc)
+    , code (0)
+    , attribs (0)
+    , size (0)
+    , alloc (0)
+    , tag (0)
+    , has_children (false)
+    , used (false)
+  {}
+};
+
+struct abbrev_table
+{
+  struct abbrev_table *next;
+  struct abbrev *abbr;
+  uint64_t offset;
+  size_t size;
+  size_t alloc;
+  bool used;           /* There are CUs using this table.  */
+
+  abbrev *find_abbrev (uint64_t abbrev_code) const;
+
+  abbrev_table ()
+    : next (NULL)
+    , abbr (NULL)
+    , offset (0)
+    , size (0)
+    , alloc (0)
+    , used (false)
+  {}
+};
+
+class check_debug_abbrev
+  : public check<check_debug_abbrev>
+{
+  section<sec_abbrev> *_m_sec_abbr;
+  read_cu_headers *_m_cu_headers;
+
+public:
+  static checkdescriptor const *descriptor ();
+
+  // offset -> abbreviations
+  typedef std::map< ::Dwarf_Off, abbrev_table> abbrev_map;
+  abbrev_map const abbrevs;
+
+  check_debug_abbrev (checkstack &stack, dwarflint &lint);
+  static form const *check_form (dwarf_version const *ver,
+                                attribute const *attr,
+                                int form_name,
+                                locus const &loc,
+                                bool indirect);
+
+  ~check_debug_abbrev ();
+};
+
+#endif//DWARFLINT_CHECK_DEBUG_ABBREV_HH
diff --git a/dwarflint/check_debug_abbrev_i.hh b/dwarflint/check_debug_abbrev_i.hh
new file mode 100644 (file)
index 0000000..47c2208
--- /dev/null
@@ -0,0 +1 @@
+class check_debug_abbrev;
diff --git a/dwarflint/check_debug_aranges.cc b/dwarflint/check_debug_aranges.cc
new file mode 100644 (file)
index 0000000..8a503ce
--- /dev/null
@@ -0,0 +1,448 @@
+/* Low-level checking of .debug_aranges.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+// xxx drop as soon as not necessary
+#define __STDC_FORMAT_MACROS
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <inttypes.h>
+
+#include "elf_file.hh"
+#include "sections.hh"
+#include "check_debug_aranges.hh"
+#include "check_debug_info.hh"
+#include "check_debug_loc_range.hh"
+#include "cu_coverage.hh"
+#include "checked_read.hh"
+#include "misc.hh"
+#include "pri.hh"
+
+char const *
+locus_simple_fmt::cudie_n ()
+{
+  return "CU DIE";
+}
+
+std::string
+arange_locus::format (bool brief) const
+{
+  std::stringstream ss;
+  if (!brief)
+    ss << section_name[sec_aranges] << ": ";
+
+  if (_m_arange_offset != (Dwarf_Off)-1)
+    ss << "arange 0x" << std::hex << _m_arange_offset;
+  else if (_m_table_offset != (Dwarf_Off)-1)
+    ss << "table " << std::dec << _m_table_offset;
+  else
+    ss << "arange";
+
+  if (_m_cudie_locus != NULL)
+    ss << " (" << _m_cudie_locus->format (true) << ')';
+
+  return ss.str ();
+}
+
+checkdescriptor const *
+check_debug_aranges::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_aranges")
+     .groups ("@low")
+     .description (
+"Checks for low-level structure of .debug_aranges.  In addition it "
+"checks:\n"
+" - that relocations are valid.  In ET_REL files that certain fields "
+"are relocated\n"
+" - for dangling and duplicate CU references\n"
+" - for garbage inside padding\n"
+" - for zero-length ranges\n"
+" - that the ranges cover all the address range covered by CUs\n"
+                  ));
+  return &cd;
+}
+
+static reg<check_debug_aranges> reg_debug_aranges;
+
+static struct cu *
+cu_find_cu (struct cu *cu_chain, uint64_t offset)
+{
+  for (struct cu *it = cu_chain; it != NULL; it = it->next)
+    if (it->head->offset == offset)
+      return it;
+  return NULL;
+}
+
+#define PRI_CU "CU 0x%" PRIx64
+
+namespace
+{
+  struct hole_user
+  {
+    elf_file *elf;
+    section_id id;
+    char const *what;
+    bool reverse;
+
+    hole_user (elf_file *a_elf, section_id a_id,
+              char const *a_what, bool a_reverse)
+      : elf (a_elf)
+      , id (a_id)
+      , what (a_what)
+      , reverse (a_reverse)
+    {}
+  };
+}
+
+static bool
+hole (uint64_t start, uint64_t length, void *user)
+{
+  /* We need to check alignment vs. the covered section.  Find
+     where the hole lies.  */
+  ::hole_user &info = *static_cast< ::hole_user *> (user);
+  struct elf_file *elf = info.elf;
+  struct sec *sec = NULL;
+  for (size_t i = 1; i < elf->size; ++i)
+    {
+      struct sec *it = elf->sec + i;
+      GElf_Shdr *shdr = &it->shdr;
+      Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size;
+      if (start >= shdr->sh_addr && start + length < s_end)
+       {
+         sec = it;
+         /* Simply assume the first section that the hole
+            intersects. */
+         break;
+       }
+    }
+
+  if (sec == NULL
+      || !necessary_alignment (start, length, sec->shdr.sh_addralign))
+    {
+      char buf[128];
+      char const *what = info.what;
+      char const *cu = "CU DIEs";
+      if (info.reverse)
+       {
+         char const *tmp = what;
+         what = cu;
+         cu = tmp;
+       }
+      wr_message (section_locus (info.id), mc_aranges | mc_impact_3)
+       << "addresses " << range_fmt (buf, sizeof (buf), start, start + length)
+       << " are covered with " << cu << ", but not with " << what << "."
+       << std::endl;
+    }
+
+  if (sec == NULL)
+    wr_error (NULL, "Couldn't find the section containing the above hole.\n");
+
+  return true;
+}
+
+static void
+compare_coverage_1 (struct elf_file *file,
+                   struct coverage *coverage, struct coverage *other,
+                   enum section_id id, char const *what,
+                   bool reverse)
+{
+  struct coverage cov = *coverage - *other;
+  hole_user info (file, id, what, reverse);
+  cov.find_ranges (hole, &info);
+}
+
+static void
+compare_coverage (struct elf_file *file,
+                 struct coverage *coverage, struct coverage *other,
+                 enum section_id id, char const *what)
+{
+  compare_coverage_1 (file, coverage, other, id, what, false);
+  compare_coverage_1 (file, other, coverage, id, what, true);
+}
+
+inline static void
+aranges_coverage_add (struct coverage *aranges_coverage,
+                     uint64_t begin, uint64_t length,
+                     locus const &loc)
+{
+  if (aranges_coverage->is_overlap (begin, length))
+    {
+      char buf[128];
+      /* Not a show stopper, this shouldn't derail high-level.  */
+      wr_message (loc, mc_aranges | mc_impact_2 | mc_error)
+       << "the range " << range_fmt (buf, sizeof buf, begin, begin + length)
+       << " overlaps with another one." << std::endl;
+    }
+
+  aranges_coverage->add (begin, length);
+}
+
+/* COVERAGE is portion of address space covered by CUs (either via
+   low_pc/high_pc pairs, or via DW_AT_ranges references).  If
+   non-NULL, analysis of arange coverage is done against that set. */
+static bool
+check_aranges_structural (struct elf_file *file,
+                         struct sec *sec,
+                         struct cu *cu_chain,
+                         struct coverage *coverage)
+{
+  struct read_ctx ctx;
+  read_ctx_init (&ctx, sec->data, file->other_byte_order);
+
+  bool retval = true;
+
+  struct coverage *aranges_coverage
+    = coverage != NULL ? new struct coverage () : NULL;
+
+  while (!read_ctx_eof (&ctx))
+    {
+      arange_locus where (read_ctx_get_offset (&ctx));
+      const unsigned char *atab_begin = ctx.ptr;
+
+      /* Size.  */
+      uint32_t size32;
+      uint64_t size;
+      int offset_size;
+      if (!read_ctx_read_4ubyte (&ctx, &size32))
+       {
+         wr_error (&where, ": can't read table length.\n");
+         return false;
+       }
+      if (!read_size_extra (&ctx, size32, &size, &offset_size, where))
+       return false;
+
+      struct read_ctx sub_ctx;
+      const unsigned char *atab_end = ctx.ptr + size;
+      if (false)
+       {
+       next:
+         if (!read_ctx_skip (&ctx, size))
+           /* A "can't happen" error.  */
+           goto not_enough;
+         continue;
+       }
+      if (!read_ctx_init_sub (&sub_ctx, &ctx, atab_begin, atab_end))
+       {
+       not_enough:
+         wr_error (&where, PRI_NOT_ENOUGH, "next table");
+         return false;
+       }
+
+      sub_ctx.ptr = ctx.ptr;
+
+      /* Version.  */
+      uint16_t version;
+      if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+       {
+         wr_error (&where, ": can't read version.\n");
+         retval = false;
+         goto next;
+       }
+      if (!supported_version (version, 1, where, 2))
+       {
+         retval = false;
+         goto next;
+       }
+
+      /* CU offset.  */
+      uint64_t cu_offset;
+      uint64_t ctx_offset = sub_ctx.ptr - ctx.begin;
+      if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_offset))
+       {
+         wr_error (&where, ": can't read debug info offset.\n");
+         retval = false;
+         goto next;
+       }
+
+      struct relocation *rel;
+      if ((rel = relocation_next (&sec->rel, ctx_offset,
+                                 where, skip_mismatched)))
+       relocate_one (file, &sec->rel, rel, offset_size,
+                     &cu_offset, where, sec_info, NULL);
+      else if (file->ehdr.e_type == ET_REL)
+       wr_message (mc_impact_2 | mc_aranges | mc_reloc | mc_header, &where,
+                   PRI_LACK_RELOCATION, "debug info offset");
+
+      struct cu *cu = NULL;
+      if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL)
+       wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset);
+
+      cudie_locus cudie_loc (cu != NULL ? cu->cudie_offset : -1);
+      if (cu != NULL)
+       {
+         where.set_cudie (&cudie_loc);
+         if (cu->has_arange)
+           wr_error (where)
+             << "there has already been arange section for this CU."
+             << std::endl;
+         else
+           cu->has_arange = true;
+       }
+
+      /* Address size.  */
+      int address_size;
+      error_code err = read_address_size (&sub_ctx, file->addr_64,
+                                         &address_size, where);
+      if (err != err_ok)
+       retval = false;
+      if (err == err_fatal)
+       goto next;
+
+      /* Segment size.  */
+      uint8_t segment_size;
+      if (!read_ctx_read_ubyte (&sub_ctx, &segment_size))
+       {
+         wr_error (&where, ": can't read unit segment size.\n");
+         retval = false;
+         goto next;
+       }
+      if (segment_size != 0)
+       {
+         wr_error (&where, ": dwarflint can't handle segment_size != 0.\n");
+         retval = false;
+         goto next;
+       }
+
+
+      /* 7.20: The first tuple following the header in each set begins
+        at an offset that is a multiple of the size of a single tuple
+        (that is, twice the size of an address). The header is
+        padded, if necessary, to the appropriate boundary.  */
+      const uint8_t tuple_size = 2 * address_size;
+      uint64_t off = read_ctx_get_offset (&sub_ctx);
+      if ((off % tuple_size) != 0)
+       {
+         uint64_t noff = ((off / tuple_size) + 1) * tuple_size;
+         for (uint64_t i = off; i < noff; ++i)
+           {
+             uint8_t c;
+             if (!read_ctx_read_ubyte (&sub_ctx, &c))
+               {
+                 wr_error (&where,
+                           ": section ends after the header, "
+                           "but before the first entry.\n");
+                 retval = false;
+                 goto next;
+               }
+             if (c != 0)
+               wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
+                           ": non-zero byte at 0x%" PRIx64
+                           " in padding before the first entry.\n",
+                           read_ctx_get_offset (&sub_ctx));
+           }
+       }
+      assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0);
+
+      while (!read_ctx_eof (&sub_ctx))
+       {
+         /* We would like to report aranges the same way that readelf
+            does.  But readelf uses index of the arange in the array
+            as returned by dwarf_getaranges, which sorts the aranges
+            beforehand.  We don't want to disturb the memory this
+            way, the better to catch structural errors accurately.
+            So report arange offset instead.  If this becomes a
+            problem, we will achieve this by two-pass analysis.  */
+         where.set_arange (read_ctx_get_offset (&sub_ctx));
+
+         /* Record address.  */
+         uint64_t address;
+         ctx_offset = sub_ctx.ptr - ctx.begin;
+         bool address_relocated = false;
+         if (!read_ctx_read_var (&sub_ctx, address_size, &address))
+           {
+             wr_error (&where, ": can't read address field.\n");
+             retval = false;
+             goto next;
+           }
+
+         if ((rel = relocation_next (&sec->rel, ctx_offset,
+                                     where, skip_mismatched)))
+           {
+             address_relocated = true;
+             relocate_one (file, &sec->rel, rel, address_size,
+                           &address, where, rel_target::rel_address, NULL);
+           }
+         else if (file->ehdr.e_type == ET_REL && address != 0)
+           wr_message (mc_impact_2 | mc_aranges | mc_reloc, &where,
+                       PRI_LACK_RELOCATION, "address field");
+
+         /* Record length.  */
+         uint64_t length;
+         if (!read_ctx_read_var (&sub_ctx, address_size, &length))
+           {
+             wr_error (&where, ": can't read length field.\n");
+             retval = false;
+             goto next;
+           }
+
+         if (address == 0 && length == 0 && !address_relocated)
+           break;
+
+         if (length == 0)
+           /* DWARF 3 spec, 6.1.2 Lookup by Address: Each descriptor
+              is a pair consisting of the beginning address [...],
+              followed by the _non-zero_ length of that range.  */
+           wr_error (&where, ": zero-length address range.\n");
+         /* Skip coverage analysis if we have errors.  */
+         else if (retval && aranges_coverage != NULL)
+           aranges_coverage_add (aranges_coverage, address, length, where);
+       }
+
+      if (sub_ctx.ptr != sub_ctx.end)
+       {
+         uint64_t start, end;
+         section_locus wh (sec_aranges);
+         if (read_check_zero_padding (&sub_ctx, &start, &end))
+           wr_message_padding_0 (mc_aranges, wh, start, end);
+         else
+           {
+             wr_message_padding_n0 (mc_aranges | mc_error, wh,
+                                    start, start + size);
+             retval = false;
+           }
+       }
+
+      goto next;
+    }
+
+  if (aranges_coverage != NULL)
+    {
+      compare_coverage (file, coverage, aranges_coverage,
+                       sec_aranges, "aranges");
+      delete aranges_coverage;
+    }
+
+  return retval;
+}
+
+check_debug_aranges::check_debug_aranges (checkstack &stack, dwarflint &lint)
+  : _m_sec_aranges (lint.check (stack, _m_sec_aranges))
+  , _m_info (lint.toplev_check (stack, _m_info))
+  , _m_cu_coverage (lint.toplev_check (stack, _m_cu_coverage))
+{
+  coverage *cov = _m_cu_coverage != NULL ? &_m_cu_coverage->cov : NULL;
+
+  if (!check_aranges_structural (&_m_sec_aranges->file,
+                                &_m_sec_aranges->sect,
+                                _m_info != NULL
+                                  ? &_m_info->cus.front () : NULL,
+                                cov))
+    throw check_base::failed ();
+}
diff --git a/dwarflint/check_debug_aranges.hh b/dwarflint/check_debug_aranges.hh
new file mode 100644 (file)
index 0000000..7393b8d
--- /dev/null
@@ -0,0 +1,98 @@
+/* Low-level checking of .debug_aranges.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECK_DEBUG_ARANGES_HH
+#define DWARFLINT_CHECK_DEBUG_ARANGES_HH
+
+#include "checks.hh"
+#include "sections_i.hh"
+#include "check_debug_info_i.hh"
+#include "cu_coverage_i.hh"
+
+namespace locus_simple_fmt
+{
+  char const *cudie_n ();
+};
+
+class cudie_locus
+  : public fixed_locus<sec_info,
+                      locus_simple_fmt::cudie_n,
+                      locus_simple_fmt::dec>
+{
+  typedef fixed_locus<sec_info,
+                     locus_simple_fmt::cudie_n,
+                     locus_simple_fmt::dec> _super_t;
+public:
+  template <class T>
+  cudie_locus (T const &die)
+    : _super_t (die.offset ())
+  {}
+
+  cudie_locus (Dwarf_Off offset)
+    : _super_t (offset)
+  {}
+};
+
+class arange_locus
+  : public locus
+{
+  Dwarf_Off _m_table_offset;
+  Dwarf_Off _m_arange_offset;
+  locus const *_m_cudie_locus;
+
+public:
+  explicit arange_locus (Dwarf_Off table_offset = -1,
+                        Dwarf_Off arange_offset = -1)
+    : _m_table_offset (table_offset)
+    , _m_arange_offset (arange_offset)
+    , _m_cudie_locus (NULL)
+  {}
+
+  explicit arange_locus (locus const &cudie_locus)
+    : _m_table_offset (-1)
+    , _m_arange_offset (-1)
+    , _m_cudie_locus (&cudie_locus)
+  {}
+
+  void
+  set_cudie (locus const *cudie_locus)
+  {
+    _m_cudie_locus = cudie_locus;
+  }
+
+  void
+  set_arange (Dwarf_Off arange_offset)
+  {
+    _m_arange_offset = arange_offset;
+  }
+
+  std::string format (bool brief = false) const;
+};
+
+class check_debug_aranges
+  : public check<check_debug_aranges>
+{
+  section<sec_aranges> *_m_sec_aranges;
+  check_debug_info *_m_info;
+  cu_coverage *_m_cu_coverage;
+
+public:
+  static checkdescriptor const *descriptor ();
+  check_debug_aranges (checkstack &stack, dwarflint &lint);
+};
+
+#endif//DWARFLINT_CHECK_DEBUG_ARANGES_HH
diff --git a/dwarflint/check_debug_aranges_i.hh b/dwarflint/check_debug_aranges_i.hh
new file mode 100644 (file)
index 0000000..0a69813
--- /dev/null
@@ -0,0 +1 @@
+class check_debug_aranges;
diff --git a/dwarflint/check_debug_info.cc b/dwarflint/check_debug_info.cc
new file mode 100644 (file)
index 0000000..a7c5198
--- /dev/null
@@ -0,0 +1,1261 @@
+/* Routines related to .debug_info.
+
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cassert>
+#include <algorithm>
+#include "../libdw/c++/dwarf"
+
+#include "messages.hh"
+#include "dwarf_version.hh"
+#include "pri.hh"
+#include "option.hh"
+#include "sections.hh"
+#include "checked_read.hh"
+#include "check_debug_loc_range.hh"
+#include "check_debug_abbrev.hh"
+#include "check_debug_info.hh"
+#include "check_debug_line.hh"
+#include "check_debug_aranges.hh"
+
+checkdescriptor const *
+read_cu_headers::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("read_cu_headers")
+     .hidden ());
+  return &cd;
+}
+
+static void_option
+  dump_die_offsets ("Dump DIE offsets to stderr as the tree is iterated.",
+                   "dump-offsets");
+
+checkdescriptor const *
+check_debug_info::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_info")
+     .groups ("@low")
+     .schedule (false)
+     .option (dump_die_offsets)
+     .description (
+"Checks for low-level structure of .debug_info.  In addition it "
+"checks:\n"
+" - for dangling reference to .debug_abbrev section\n"
+" - that reported CU address sizes are consistent\n"
+" - that rangeptr values are aligned to CU address size\n"
+" - it is checked that DW_AT_low_pc and DW_AT_high_pc are relocated "
+"consistently\n"
+" - that DIE references are well formed (both intra-CU and inter-CU) "
+"and that local reference isn't needlessly formed as global\n"
+" - that .debug_string references are well formed and referred strings "
+"are properly NUL-terminated\n"
+" - that referenced abbreviations actually exist\n"
+" - that DIEs with children have the DW_AT_sibling attribute and that "
+"the sibling actually is at the address reported at that attribute\n"
+" - that the DIE chain is terminated\n"
+" - that the last sibling in chain has no DW_AT_sibling attribute\n"
+" - that the DIE with children actually has children (i.e. that the "
+"chain is not empty)\n"
+" - for format constraints (such as that there are no 64-bit CUs inside "
+"DWARF 2 file)\n"
+" - in 32-bit CUs, that location attributes are not formed with "
+"DW_FORM_data8\n"
+" - all the attribute checks done by check_debug_abbrev are done here "
+"for attributes with DW_FORM_indirect.  Indirect form is forbidden "
+"to be again indirect\n"
+" - that all abbreviations are used\n"
+" - that relocations are valid.  In ET_REL files that certain fields "
+"are relocated\n"
+                  ));
+  return &cd;
+}
+
+static reg<check_debug_info> reg_debug_info;
+
+namespace
+{
+  bool
+  check_category (enum message_category cat)
+  {
+    return message_accept (&warning_criteria, cat);
+  }
+
+  bool
+  check_die_references (cu *cu, ref_record *die_refs)
+  {
+    bool retval = true;
+    for (ref_record::const_iterator it = die_refs->begin ();
+        it != die_refs->end (); ++it)
+      if (!cu->die_addrs.has_addr (it->addr))
+       {
+         wr_error (it->who)
+           << "unresolved reference to " << pri::DIE (it->addr)
+           << '.' << std::endl;
+         retval = false;
+       }
+    return retval;
+  }
+
+  bool
+  check_global_die_references (struct cu *cu_chain)
+  {
+    bool retval = true;
+    for (struct cu *it = cu_chain; it != NULL; it = it->next)
+      for (ref_record::const_iterator rt = it->die_refs.begin ();
+          rt != it->die_refs.end (); ++rt)
+       {
+         struct cu *ref_cu = NULL;
+         for (struct cu *jt = cu_chain; jt != NULL; jt = jt->next)
+           if (jt->die_addrs.has_addr (rt->addr))
+             {
+               ref_cu = jt;
+               break;
+             }
+
+         if (ref_cu == NULL)
+           {
+             wr_error (rt->who)
+               << "unresolved (non-CU-local) reference to "
+               << pri::hex (rt->addr) << '.' << std::endl;
+             retval = false;
+           }
+         else if (ref_cu == it)
+           /* This is technically not a problem, so long as the
+              reference is valid, which it is.  But warn about this
+              anyway, perhaps local reference could be formed on
+              smaller number of bytes.  */
+           wr_message (rt->who, mc_impact_2 | mc_acc_suboptimal | mc_die_rel)
+             << "local reference to " << pri::DIE (rt->addr)
+             << " formed as global." << std::endl;
+       }
+
+    return retval;
+  }
+
+  std::vector <cu_head>
+  read_info_headers (struct elf_file *file,
+                    struct sec *sec,
+                    struct relocation_data *reloc)
+  {
+    struct read_ctx ctx;
+    read_ctx_init (&ctx, sec->data, file->other_byte_order);
+    uint64_t off_start, off_end;
+    bool fail = false;
+
+    std::vector <cu_head> ret;
+    while (!read_ctx_eof (&ctx))
+      {
+       const unsigned char *cu_begin = ctx.ptr;
+       uint64_t offset = read_ctx_get_offset (&ctx);
+       cu_head head (offset);
+
+       /* Reading CU head is a bit tricky, because we don't know if
+          we have run into (superfluous but allowed) zero padding
+          between CUs.  */
+
+       if (!read_ctx_need_data (&ctx, 4)
+           && read_check_zero_padding (&ctx, &off_start, &off_end))
+         {
+           wr_message_padding_0 (mc_info | mc_header, head.where,
+                                 off_start, off_end);
+           break;
+         }
+
+       /* CU length.  In DWARF 2, (uint32_t)-1 is simply a CU of that
+          length.  In DWARF 3+ that's an escape for 64bit length.
+          Unfortunately to read CU version, we have to get through
+          this field.  So we just assume that (uint32_t)-1 is an
+          escape in all cases.  */
+       uint32_t size32;
+       if (!read_ctx_read_4ubyte (&ctx, &size32))
+         {
+           wr_error (head.where) << "can't read CU length." << std::endl;
+           throw check_base::failed ();
+         }
+       if (size32 == 0
+           && read_check_zero_padding (&ctx, &off_start, &off_end))
+         {
+           wr_message_padding_0 (mc_info | mc_header, head.where,
+                                 off_start, off_end);
+           break;
+         }
+
+       Dwarf_Off cu_size;
+       if (!read_size_extra (&ctx, size32, &cu_size,
+                             &head.offset_size, head.where))
+         throw check_base::failed ();
+
+       if (!read_ctx_need_data (&ctx, cu_size))
+         {
+           wr_error (head.where)
+             << "section doesn't have enough data to read CU of size "
+             << cu_size << '.' << std::endl;
+           throw check_base::failed ();
+         }
+
+       /* CU size captures the size from the end of the length field
+          to the end of the CU.  */
+       const unsigned char *cu_end = ctx.ptr + cu_size;
+
+       /* Version.  */
+       uint16_t version;
+       if (!read_ctx_read_2ubyte (&ctx, &version))
+         {
+           wr_error (head.where) << "can't read version." << std::endl;
+           throw check_base::failed ();
+         }
+       if (dwarf_version::get (version) == NULL)
+         {
+           wr_error (head.where) << "unsupported CU version "
+                            << version << '.' << std::endl;
+           throw check_base::failed ();
+         }
+       if (version == 2 && head.offset_size == 8) // xxx?
+         /* Keep going.  It's a standard violation, but we may still
+            be able to read the unit under consideration and do
+            high-level checks.  */
+         wr_error (head.where) << "invalid 64-bit unit in DWARF 2 format.\n";
+       head.version = version;
+
+       /* Abbrev table offset.  */
+       uint64_t ctx_offset = read_ctx_get_offset (&ctx);
+       if (!read_ctx_read_offset (&ctx, head.offset_size == 8,
+                                  &head.abbrev_offset))
+         {
+           wr_error (head.where)
+             << "can't read abbrev table offset." << std::endl;
+           throw check_base::failed ();
+         }
+
+       struct relocation *rel
+         = relocation_next (reloc, ctx_offset, head.where, skip_ok);
+       if (rel != NULL)
+         {
+           relocate_one (file, reloc, rel, head.offset_size,
+                         &head.abbrev_offset, head.where, sec_abbrev, NULL);
+           rel->invalid = true; // mark as invalid so it's skipped
+                                // next time we pass by this
+         }
+       else if (file->ehdr.e_type == ET_REL)
+         wr_message (head.where, mc_impact_2 | mc_info | mc_reloc)
+           << pri::lacks_relocation ("abbrev table offset") << std::endl;
+
+       /* Address size.  */
+       error_code err = read_address_size (&ctx, file->addr_64,
+                                           &head.address_size, head.where);
+       if (err == err_fatal)
+         throw check_base::failed ();
+       else if (err == err_nohl)
+         fail = true;
+
+       head.head_size = ctx.ptr - cu_begin; // Length of the headers itself.
+       head.total_size = cu_end - cu_begin; // Length including headers field.
+       head.size = head.total_size - head.head_size;
+
+       if (!read_ctx_skip (&ctx, head.size))
+         {
+           wr_error (head.where) << pri::not_enough ("next CU") << std::endl;
+           throw check_base::failed ();
+         }
+
+       ret.push_back (head);
+      }
+
+    if (fail)
+      throw check_base::failed ();
+
+    return ret;
+  }
+
+  rel_target
+  reloc_target (form const *form, attribute const *attribute)
+  {
+    switch (form->name ())
+      {
+      case DW_FORM_strp:
+       return sec_str;
+
+      case DW_FORM_addr:
+
+       switch (attribute->name ())
+         {
+         case DW_AT_low_pc:
+         case DW_AT_high_pc:
+         case DW_AT_entry_pc:
+           return rel_target::rel_exec;
+
+         case DW_AT_const_value:
+           /* Appears in some kernel modules.  It's not allowed by the
+              standard, but leave that for high-level checks.  */
+           return rel_target::rel_address;
+         };
+
+       break;
+
+      case DW_FORM_ref_addr:
+       return sec_info;
+
+      case DW_FORM_data1:
+      case DW_FORM_data2:
+       /* While these are technically legal, they are never used in
+          DWARF sections.  So better mark them as illegal, and have
+          dwarflint flag them.  */
+       return sec_invalid;
+
+      case DW_FORM_data4:
+      case DW_FORM_data8:
+      case DW_FORM_sec_offset:
+
+       switch (attribute->name ())
+         {
+         case DW_AT_stmt_list:
+           return sec_line;
+
+         case DW_AT_location:
+         case DW_AT_string_length:
+         case DW_AT_return_addr:
+         case DW_AT_data_member_location:
+         case DW_AT_frame_base:
+         case DW_AT_segment:
+         case DW_AT_static_link:
+         case DW_AT_use_location:
+         case DW_AT_vtable_elem_location:
+           return sec_loc;
+
+         case DW_AT_mac_info:
+           return sec_mac;
+
+         case DW_AT_ranges:
+           return sec_ranges;
+         }
+
+       break;
+
+      case DW_FORM_string:
+      case DW_FORM_ref1:
+      case DW_FORM_ref2:
+      case DW_FORM_ref4:
+       /* Shouldn't be relocated.  */
+       return sec_invalid;
+
+      case DW_FORM_sdata:
+      case DW_FORM_udata:
+      case DW_FORM_flag:
+      case DW_FORM_flag_present:
+      case DW_FORM_ref_udata:
+       assert (!"Can't be relocated!");
+
+      case DW_FORM_block1:
+      case DW_FORM_block2:
+      case DW_FORM_block4:
+      case DW_FORM_block:
+       assert (!"Should be handled specially!");
+      };
+
+    std::cout << "XXX don't know how to handle form=" << *form
+             << ", at=" << *attribute << std::endl;
+
+    return rel_target::rel_value;
+  }
+
+  struct value_check_cb_ctx
+  {
+    struct read_ctx *const ctx;
+    die_locus const *where;
+    struct cu *const cu;
+    ref_record *local_die_refs;
+    Elf_Data *strings;
+    struct coverage *strings_coverage;
+    struct coverage *pc_coverage;
+    bool *need_rangesp;
+    int *retval_p;
+  };
+
+  typedef void (*value_check_cb_t) (uint64_t addr,
+                                   struct value_check_cb_ctx const *ctx);
+
+  /* Callback for local DIE references.  */
+  void
+  check_die_ref_local (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    assert (ctx->ctx->end > ctx->ctx->begin);
+    if (addr > (uint64_t)(ctx->ctx->end - ctx->ctx->begin))
+      {
+       wr_error (*ctx->where)
+         << "invalid reference outside the CU: " << pri::hex (addr)
+         << '.' << std::endl;
+       return;
+      }
+
+    if (ctx->local_die_refs != NULL)
+      /* Address holds a CU-local reference, so add CU offset
+        to turn it into section offset.  */
+      ctx->local_die_refs->push_back (ref (addr + ctx->cu->head->offset,
+                                          *ctx->where));
+  }
+
+  /* Callback for global DIE references.  */
+  void
+  check_die_ref_global (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    ctx->cu->die_refs.push_back (ref (addr, *ctx->where));
+  }
+
+  /* Callback for strp values.  */
+  void
+  check_strp (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    if (ctx->strings == NULL)
+      wr_error (*ctx->where)
+       << "strp attribute, but no .debug_str data." << std::endl;
+    else if (addr >= ctx->strings->d_size)
+      wr_error (*ctx->where)
+       << "invalid offset outside .debug_str: " << pri::hex (addr)
+       << '.' << std::endl;
+    else
+      {
+       /* Record used part of .debug_str.  */
+       const char *buf = static_cast <const char *> (ctx->strings->d_buf);
+       const char *startp = buf + addr;
+       const char *data_end = buf + ctx->strings->d_size;
+       const char *strp = startp;
+       while (strp < data_end && *strp != 0)
+         ++strp;
+       if (strp == data_end)
+         {
+           wr_error (*ctx->where)
+             << "string at .debug_str: " << pri::hex (addr)
+             << " is not zero-terminated." << std::endl;
+           *ctx->retval_p = -2;
+         }
+
+       if (ctx->strings_coverage != NULL)
+         ctx->strings_coverage->add (addr, strp - startp + 1);
+      }
+  }
+
+  /* Callback for rangeptr values.  */
+  void
+  check_rangeptr (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    if ((value % ctx->cu->head->address_size) != 0)
+      wr_message (*ctx->where, mc_ranges | mc_impact_2)
+       << "rangeptr value " << pri::hex (value)
+       << " not aligned to CU address size." << std::endl;
+    *ctx->need_rangesp = true;
+    ctx->cu->range_refs.push_back (ref (value, *ctx->where));
+  }
+
+  /* Callback for lineptr values.  */
+  void
+  check_lineptr (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    if (ctx->cu->stmt_list.addr != (uint64_t)-1)
+      wr_error (*ctx->where)
+       << "DW_AT_stmt_list mentioned twice in a CU." << std::endl;
+    ctx->cu->stmt_list = ref (value, *ctx->where);
+  }
+
+  /* Callback for locptr values.  */
+  void
+  check_locptr (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    ctx->cu->loc_refs.push_back (ref (value, *ctx->where));
+  }
+
+  void
+  check_decl_file (uint64_t value, struct value_check_cb_ctx const *ctx)
+  {
+    ctx->cu->decl_file_refs.push_back (ref (value, *ctx->where));
+  }
+
+  /* The real sibling checking takes place down in read_die_chain.
+     Here we just make sure that the value is non-zero.  That value is
+     clearly invalid, and we use it to mark absent DW_AT_sibling.  */
+  void
+  check_sibling_non0 (uint64_t addr, struct value_check_cb_ctx const *ctx)
+  {
+    if (addr == 0)
+      {
+       wr_error (*ctx->where)
+         << "has a value of 0." << std::endl;
+       // Don't let this up.
+       *ctx->retval_p = -2;
+      }
+  }
+
+  /*
+    Returns:
+    -2 in case of error that we have to note and return, but for now
+       we can carry on
+    -1 in case of error
+    +0 in case of no error, but the chain only consisted of a
+       terminating zero die.
+    +1 in case some dies were actually loaded
+  */
+  int
+  read_die_chain (dwarf_version const *ver,
+                 elf_file const &file,
+                 read_ctx *ctx,
+                 cu *cu,
+                 abbrev_table const *abbrevs,
+                 Elf_Data *strings,
+                 ref_record *local_die_refs,
+                 coverage *strings_coverage,
+                 relocation_data *reloc,
+                 coverage *pc_coverage,
+                 bool *need_rangesp,
+                 unsigned level)
+  {
+    bool got_die = false;
+    uint64_t sibling_addr = 0;
+    uint64_t die_off, prev_die_off = 0;
+    struct abbrev *abbrev = NULL;
+    unsigned long die_count = 0;
+    int retval = 0;
+
+    struct value_check_cb_ctx cb_ctx = {
+      ctx, NULL, cu,
+      local_die_refs,
+      strings, strings_coverage,
+      pc_coverage,
+      need_rangesp,
+      &retval
+    };
+
+    while (!read_ctx_eof (ctx))
+      {
+       die_off = read_ctx_get_offset (ctx);
+       /* Shift reported DIE offset by CU offset, to match the way
+          readelf reports DIEs.  */
+       die_locus where (cu->head->offset + die_off);
+       cb_ctx.where = &where;
+
+       uint64_t abbr_code;
+
+       if (!checked_read_uleb128 (ctx, &abbr_code, where, "abbrev code"))
+         return -1;
+
+#define DEF_PREV_WHERE die_locus prev_where (cu->head->offset + prev_die_off)
+
+       /* Check sibling value advertised last time through the loop.  */
+       if (sibling_addr != 0)
+         {
+           if (abbr_code == 0)
+             {
+               DEF_PREV_WHERE;
+               wr_error (&prev_where,
+                         ": is the last sibling in chain, "
+                         "but has a DW_AT_sibling attribute.\n");
+               /* dwarf_siblingof uses DW_AT_sibling to look for
+                  sibling DIEs.  The value can't be right (there _is_
+                  no sibling), so don't let this up.  */
+               retval = -2;
+             }
+           else if (sibling_addr != die_off)
+             {
+               DEF_PREV_WHERE;
+               wr_error (prev_where)
+                 << "this DIE claims that its sibling is "
+                 << pri::hex (sibling_addr) << " but it's actually "
+                 << pri::hex (die_off) << '.' << std::endl;
+               retval = -2;
+             }
+           sibling_addr = 0;
+         }
+       else if (abbr_code != 0
+                && abbrev != NULL && abbrev->has_children)
+         {
+           /* Even if it has children, the DIE can't have a sibling
+              attribute if it's the last DIE in chain.  That's the
+              reason we can't simply check this when loading
+              abbrevs.  */
+           DEF_PREV_WHERE;
+           wr_message (prev_where, mc_die_rel | mc_acc_suboptimal | mc_impact_4)
+             << "This DIE had children, but no DW_AT_sibling attribute."
+             << std::endl;
+         }
+#undef DEF_PREV_WHERE
+
+       prev_die_off = die_off;
+
+       /* The section ended.  */
+       if (abbr_code == 0)
+         break;
+
+       prev_die_off = die_off;
+       got_die = true;
+
+       /* Find the abbrev matching the code.  */
+       abbrev = abbrevs->find_abbrev (abbr_code);
+       if (abbrev == NULL)
+         {
+           wr_error (where)
+             << "abbrev section at " << pri::hex (abbrevs->offset)
+             << " doesn't contain code " << abbr_code << '.' << std::endl;
+           return -1;
+         }
+       abbrev->used = true;
+
+       if (dump_die_offsets)
+         std::cerr << "[" << level << "] "
+                   << where << ": abbrev " << abbr_code
+                   << "; DIE tag 0x" << std::hex << abbrev->tag << std::endl;
+
+       // DWARF 4 Ch. 7.5: compilation unit header [is] followed by a
+       // single DW_TAG_compile_unit or DW_TAG_partial_unit.
+       bool is_cudie = level == 0
+         && (abbrev->tag == DW_TAG_compile_unit
+             || abbrev->tag == DW_TAG_partial_unit);
+       if (level == 0)
+         {
+           if (++die_count > 1)
+             wr_error (where)
+               << "toplevel DIE chain contains more than one DIE."
+               << std::endl;
+           else if (!is_cudie)
+             {
+               wr_error (cu->head->where)
+                 << "toplevel DIE must be either compile_unit or partial_unit."
+                 << std::endl;
+               retval = -2;
+             }
+         }
+
+       cu->die_addrs.add (cu->head->offset + die_off);
+
+       uint64_t low_pc = (uint64_t)-1, high_pc = (uint64_t)-1;
+       bool low_pc_relocated = false, high_pc_relocated = false;
+       bool high_pc_relative = false;
+       GElf_Sym low_pc_symbol_mem, *low_pc_symbol = &low_pc_symbol_mem;
+       GElf_Sym high_pc_symbol_mem, *high_pc_symbol = &high_pc_symbol_mem;
+
+       /* Attribute values.  */
+       for (struct abbrev_attrib *it = abbrev->attribs;
+            it->name != 0 || it->form != 0; ++it)
+         {
+           where.set_attrib_name (it->name);
+           int form_name = it->form;
+
+           // In following, attribute may be NULL, but form never
+           // should.  We always need to know the form to be able to
+           // read .debug_info, so we fail in check_debug_abbrev if
+           // it's invalid or unknown.
+           attribute const *attribute = ver->get_attribute (it->name);
+           form const *form = ver->get_form (form_name);
+           if (attribute != NULL
+               && ver->form_class (form, attribute) == cl_indirect)
+             {
+               uint64_t value;
+               if (!read_sc_value (&value, form->width (cu->head),
+                                   ctx, where))
+                 return -1;
+               form_name = value;
+               form = check_debug_abbrev::check_form
+                 (ver, attribute, form_name, where, true);
+               // N.B. check_form emits appropriate error messages.
+               if (form == NULL)
+                 return -1;
+             }
+           assert (form != NULL);
+
+           dw_class cls = attribute != NULL
+             ? ver->form_class (form, attribute)
+             : max_dw_class;
+           if (cls == cl_indirect)
+             {
+               wr_error (&where, ": indirect form is again indirect.\n");
+               return -1;
+             }
+
+           value_check_cb_t value_check_cb = NULL;
+
+           /* For checking lineptr, rangeptr, locptr.  */
+           bool check_someptr = false;
+           enum message_category extra_mc = mc_none;
+
+           uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->head->offset;
+           bool type_is_rel = file.ehdr.e_type == ET_REL;
+
+           /* Whether the value should be relocated first.  Note that
+              relocations are really required only in REL files, so
+              missing relocations are not warned on even with
+              rel_require, unless type_is_rel.  */
+           enum
+           {
+             rel_no,           // don't allow a relocation
+             rel_require,      // require a relocation
+             rel_nonzero,      // require a relocation if value != 0
+           } relocate = rel_no;
+
+           /* Point to variable that you want to copy relocated value
+              to.  */
+           uint64_t *valuep = NULL;
+
+           /* Point to variable that you want set to `true' in case the
+              value was relocated.  */
+           bool *relocatedp = NULL;
+
+           /* Point to variable that you want set to symbol that the
+              relocation was made against.  */
+           GElf_Sym **symbolp = NULL;
+
+           static dw_class_set ref_classes
+             (cl_reference, cl_loclistptr, cl_lineptr, cl_macptr,
+              cl_rangelistptr);
+
+           if (form != NULL && cls != max_dw_class && ref_classes.test (cls))
+             {
+               form_bitness_t bitness = form->bitness ();
+               if ((bitness == fb_32 && cu->head->offset_size == 8)
+                   || (bitness == fb_64 && cu->head->offset_size == 4))
+                 wr_error (where)
+                   << "reference attribute with form \""
+                   << elfutils::dwarf::forms::name (form_name) << "\" in "
+                   << (8 * cu->head->offset_size) << "-bit CU."
+                   << std::endl;
+             }
+
+           /* Setup pointer checking.  */
+           switch (cls)
+             {
+             case cl_loclistptr:
+               check_someptr = true;
+               value_check_cb = check_locptr;
+               extra_mc = mc_loc;
+               break;
+
+             case cl_rangelistptr:
+               check_someptr = true;
+               value_check_cb = check_rangeptr;
+               extra_mc = mc_ranges;
+               break;
+
+             case cl_lineptr:
+               check_someptr = true;
+               value_check_cb = check_lineptr;
+               extra_mc = mc_line;
+               break;
+
+             default:
+               ;
+             }
+
+           /* Setup low_pc / high_pc checking.  */
+           switch (it->name)
+             {
+             case DW_AT_low_pc:
+               relocatedp = &low_pc_relocated;
+               symbolp = &low_pc_symbol;
+               valuep = &low_pc;
+               break;
+
+             case DW_AT_high_pc:
+               relocatedp = &high_pc_relocated;
+               symbolp = &high_pc_symbol;
+               valuep = &high_pc;
+               if (cls == cl_constant)
+                 high_pc_relative = true;
+               else if (cls != cl_address)
+                 {
+                   wr_error (&where, ": DW_AT_high_pc in unknown form.\n");
+                   retval = -2;
+                 }
+               break;
+
+             case DW_AT_decl_file:
+               value_check_cb = check_decl_file;
+               break;
+             }
+
+           /* Setup per-form checking & relocation.  */
+           switch (form_name)
+             {
+             case DW_FORM_strp:
+               value_check_cb = check_strp;
+             case DW_FORM_sec_offset:
+               relocate = rel_require;
+               break;
+
+             case DW_FORM_ref_addr:
+               value_check_cb = check_die_ref_global;
+             case DW_FORM_addr:
+               /* In non-rel files, neither addr, nor ref_addr /need/
+                  a relocation.  */
+               relocate = rel_nonzero;
+               break;
+
+             case DW_FORM_ref_udata:
+             case DW_FORM_ref1:
+             case DW_FORM_ref2:
+             case DW_FORM_ref4:
+             case DW_FORM_ref8:
+               value_check_cb = check_die_ref_local;
+               break;
+
+             case DW_FORM_data4:
+             case DW_FORM_data8:
+               if (check_someptr)
+                 relocate = rel_require;
+               break;
+             }
+
+           /* Attribute value.  */
+           uint64_t value;
+           read_ctx block;
+
+           storage_class_t storclass = form->storage_class ();
+           if (!read_generic_value (ctx, form->width (cu->head), storclass,
+                                    where, &value, &block))
+             {
+               // Note that for fw_uleb and fw_sleb we report the
+               // error the second time now.
+               wr_error (where)
+                 << "can't read value of attribute "
+                 << elfutils::dwarf::attributes::name (it->name)
+                 << '.' << std::endl;
+               return -1;
+             }
+           if (storclass == sc_block)
+             {
+               if (cls == cl_exprloc)
+                 {
+                   uint64_t expr_start
+                     = cu->head->offset + read_ctx_get_offset (ctx) - value;
+                   // xxx should we disallow relocation of length
+                   // field?  See check_debug_loc_range::op_read_form
+                   if (!check_location_expression
+                       (ver, file, &block, cu,
+                        expr_start, reloc, value, where))
+                     return -1;
+                 }
+               else
+                 relocation_skip (reloc, read_ctx_get_offset (ctx),
+                                  where, skip_mismatched);
+             }
+
+           /* Relocate the value if appropriate.  */
+           struct relocation *rel;
+           if ((rel = relocation_next (reloc, ctx_offset,
+                                       where, skip_mismatched)))
+             {
+               if (relocate == rel_no)
+                 wr_message (where, (mc_impact_4 | mc_die_other
+                                     | mc_reloc | extra_mc))
+                   << "unexpected relocation of "
+                   << elfutils::dwarf::forms::name (form_name)
+                   << '.' << std::endl;
+
+               if (attribute != NULL)
+                 {
+                   form_width_t width = form->width (cu->head);
+                   relocate_one (&file, reloc, rel, width, &value, where,
+                                 reloc_target (form, attribute), symbolp);
+                 }
+
+               if (relocatedp != NULL)
+                 *relocatedp = true;
+             }
+           else
+             {
+               if (symbolp != NULL)
+                 memset (*symbolp, 0, sizeof (**symbolp));
+               if (type_is_rel
+                   && (relocate == rel_require
+                       || (relocate == rel_nonzero
+                           && value != 0)))
+                 wr_message (where, (mc_impact_2 | mc_die_other
+                                     | mc_reloc | extra_mc))
+                   << pri::lacks_relocation
+                       (elfutils::dwarf::forms::name (form_name))
+                   << std::endl;
+             }
+
+           /* Dispatch value checking.  */
+           if (it->name == DW_AT_sibling)
+             {
+               /* Full-blown DIE reference checking is too heavy-weight
+                  and not practical (error messages wise) for checking
+                  siblings.  */
+               assert (value_check_cb == check_die_ref_local
+                       || value_check_cb == check_die_ref_global);
+               value_check_cb = check_sibling_non0;
+               valuep = &sibling_addr;
+             }
+
+           if (value_check_cb != NULL)
+             value_check_cb (value, &cb_ctx);
+
+           /* Store the relocated value.  Note valuep may point to
+              low_pc or high_pc.  */
+           if (valuep != NULL)
+             *valuep = value;
+         }
+       where.set_attrib_name (-1);
+
+       if (high_pc != (uint64_t)-1 && low_pc != (uint64_t)-1
+           && high_pc_relative)
+         {
+           if (high_pc_relocated)
+             wr_message (where, mc_die_other | mc_impact_2 | mc_reloc)
+               << "DW_AT_high_pc is a constant (=relative), but is relocated."
+               << std::endl;
+           high_pc += low_pc;
+         }
+
+       /* Check PC coverage.  We do that only for CU DIEs.  Any DIEs
+          lower in the tree (should) take subset of addresses taken
+          by the CU DIE.  */
+       if (is_cudie && low_pc != (uint64_t)-1)
+         {
+           cu->low_pc = low_pc;
+
+           if (high_pc != (uint64_t)-1 && high_pc > low_pc)
+             pc_coverage->add (low_pc, high_pc - low_pc);
+         }
+
+       if (high_pc != (uint64_t)-1 && low_pc != (uint64_t)-1)
+         {
+           if (!high_pc_relative && high_pc_relocated != low_pc_relocated)
+             wr_message (where, mc_die_other | mc_impact_2 | mc_reloc)
+               << "only one of DW_AT_low_pc and DW_AT_high_pc is relocated."
+               << std::endl;
+           else
+             {
+               if (!high_pc_relative)
+                 check_range_relocations (where, mc_die_other,
+                                          &file,
+                                          low_pc_symbol, high_pc_symbol,
+                                          "DW_AT_low_pc and DW_AT_high_pc");
+               /* If there is no coverage, these attributes should
+                  not ever be there.  */
+               if (low_pc > high_pc || low_pc == high_pc)
+                 wr_message (where, mc_die_other | mc_impact_3)
+                   << "DW_AT_low_pc value not below DW_AT_high_pc."
+                   << std::endl;
+             }
+         }
+
+       if (abbrev->has_children)
+         {
+           int st = read_die_chain (ver, file, ctx, cu, abbrevs, strings,
+                                    local_die_refs,
+                                    strings_coverage, reloc,
+                                    pc_coverage, need_rangesp, level + 1);
+           if (st == -1)
+             return -1;
+           else if (st == -2)
+             retval = -2;
+           else if (st == 0)
+             wr_message (mc_impact_3 | mc_acc_suboptimal | mc_die_rel,
+                         &where,
+                         ": abbrev has_children, but the chain was empty.\n");
+         }
+
+       if (read_ctx_eof (ctx))
+         {
+           if (level > 0)
+             // DWARF 4 Ch. 2.3: A chain of sibling entries is
+             // terminated by a null entry.  N.B. the CU DIE is a
+             // singleton, not part of a DIE chain.
+             wr_error (where)
+               << "DIE chain not terminated with null entry." << std::endl;
+           break;
+         }
+      }
+
+    if (sibling_addr != 0)
+      wr_error (die_locus (cu->head->offset + prev_die_off))
+       << "this DIE should have had its sibling at " << pri::hex (sibling_addr)
+       << ", but the DIE chain ended." << std::endl;
+
+    if (retval != 0)
+      return retval;
+    else
+      return got_die ? 1 : 0;
+  }
+}
+
+read_cu_headers::read_cu_headers (checkstack &stack, dwarflint &lint)
+  : _m_sec_info (lint.check (stack, _m_sec_info))
+  , cu_headers (read_info_headers (&_m_sec_info->file,
+                                  &_m_sec_info->sect,
+                                  _m_sec_info->reldata ()))
+{
+}
+
+bool
+check_debug_info::check_cu_structural (struct read_ctx *ctx,
+                                      struct cu *const cu,
+                                      Elf_Data *strings,
+                                      struct coverage *strings_coverage,
+                                      struct relocation_data *reloc)
+{
+  check_debug_abbrev::abbrev_map const &abbrev_tables = _m_abbrevs->abbrevs;
+
+  if (dump_die_offsets)
+    fprintf (stderr, "%s: CU starts\n", cu->head->where.format ().c_str ());
+  bool retval = true;
+
+  dwarf_version const *ver = dwarf_version::get (cu->head->version);
+  assert (ver != NULL);
+
+  /* Look up Abbrev table for this CU.  */
+  check_debug_abbrev::abbrev_map::const_iterator abbrev_it
+    = abbrev_tables.find (cu->head->abbrev_offset);
+  if (abbrev_it == abbrev_tables.end ())
+    {
+      wr_error (cu->head->where)
+       << "couldn't find abbrev section with offset "
+       << pri::addr (cu->head->abbrev_offset) << '.' << std::endl;
+      return false;
+    }
+  struct abbrev_table const &abbrevs = abbrev_it->second;
+
+  /* Read DIEs.  */
+  ref_record local_die_refs;
+
+  cu->cudie_offset = read_ctx_get_offset (ctx) + cu->head->offset;
+  int st = read_die_chain (ver, _m_file, ctx, cu, &abbrevs, strings,
+                          &local_die_refs, strings_coverage,
+                          (reloc != NULL && reloc->size > 0) ? reloc : NULL,
+                          &_m_cov, &_m_need_ranges, 0);
+  if (st < 0)
+    {
+      _m_abbr_skip.push_back (abbrevs.offset);
+      retval = false;
+    }
+  else if (st == 0)
+    wr_error (cu->head->where)
+      << "CU contains no DIEs." << std::endl;
+  else if (!check_die_references (cu, &local_die_refs))
+    retval = false;
+
+  return retval;
+}
+
+check_debug_info::check_debug_info (checkstack &stack, dwarflint &lint)
+  : _m_sec_info (lint.check (stack, _m_sec_info))
+  , _m_sec_str (lint.check (stack, _m_sec_str))
+  , _m_file (_m_sec_info->file)
+  , _m_abbrevs (lint.check (stack, _m_abbrevs))
+  , _m_cu_headers (lint.check (stack, _m_cu_headers))
+  , _m_need_ranges (false)
+{
+  std::vector <cu_head> const &cu_headers = _m_cu_headers->cu_headers;
+  sec &sec = _m_sec_info->sect;
+  Elf_Data *const strings = _m_sec_str->sect.data;
+
+  ref_record die_refs;
+
+  bool success = true;
+
+  coverage *strings_coverage =
+    (strings != NULL && check_category (mc_strings))
+    ? new coverage () : NULL;
+
+  struct relocation_data *reloc = sec.rel.size > 0 ? &sec.rel : NULL;
+  if (reloc != NULL)
+    relocation_reset (reloc);
+
+  struct read_ctx ctx;
+  read_ctx_init (&ctx, sec.data, _m_file.other_byte_order);
+  for (std::vector <cu_head>::const_iterator it = cu_headers.begin ();
+       it != cu_headers.end (); ++it)
+    {
+      cu_head const &head = *it;
+      cu_locus where = head.where;
+      {
+       cu cur;
+       memset (&cur, 0, sizeof (cur));
+       cur.head = &head;
+       cur.low_pc = cur.stmt_list.addr = (uint64_t)-1;
+       cur.next = (cu *)(uintptr_t)0xdead;
+       cus.push_back (cur);
+      }
+      cu &cur = cus.back ();
+
+      assert (read_ctx_need_data (&ctx, head.total_size));
+
+      // Make CU context begin just before the CU length, so that
+      // DIE offsets are computed correctly.
+      struct read_ctx cu_ctx;
+      const unsigned char *cu_end = ctx.ptr + head.total_size;
+      read_ctx_init_sub (&cu_ctx, &ctx, ctx.ptr, cu_end);
+      cu_ctx.ptr += head.head_size;
+
+      if (!check_cu_structural (&cu_ctx, &cur,
+                               strings, strings_coverage, reloc))
+       {
+         success = false;
+         break;
+       }
+
+      if (cu_ctx.ptr != cu_ctx.end)
+       {
+         uint64_t off_start, off_end;
+         if (read_check_zero_padding (&cu_ctx, &off_start, &off_end))
+           wr_message_padding_0 (mc_info, where, off_start, off_end);
+         else
+           {
+             // Garbage coordinates:
+             uint64_t start = read_ctx_get_offset (&ctx) + off_start;
+             uint64_t end = read_ctx_get_offset (&ctx) + head.total_size;
+             wr_message_padding_n0 (mc_info, where, start, end);
+           }
+       }
+
+      int i = read_ctx_skip (&ctx, head.total_size);
+      assert (i);
+    }
+
+  if (success)
+    {
+      section_locus wh (sec_info);
+      if (ctx.ptr != ctx.end)
+       /* Did we read up everything?  */
+       wr_message (mc_die_other | mc_impact_4, &wh,
+                   ": CU lengths don't exactly match Elf_Data contents.");
+      else
+       /* Did we consume all the relocations?  */
+       relocation_skip_rest (&sec.rel, wh);
+
+      /* If we managed to read up everything, now do abbrev usage
+        analysis.  */
+      for (check_debug_abbrev::abbrev_map::const_iterator it
+            = _m_abbrevs->abbrevs.begin ();
+          it != _m_abbrevs->abbrevs.end (); ++it)
+       if (it->second.used
+           && std::find (_m_abbr_skip.begin (), _m_abbr_skip.end (),
+                         it->first) == _m_abbr_skip.end ())
+         for (size_t i = 0; i < it->second.size; ++i)
+           if (!it->second.abbr[i].used)
+             wr_message (it->second.abbr[i].where,
+                         mc_impact_3 | mc_acc_bloat | mc_abbrevs)
+               << "abbreviation is never used." << std::endl;
+    }
+
+  // re-link CUs so that they form a chain again.  This is to
+  // interface with legacy code.
+  {
+    cu *last = NULL;
+    for (std::vector<cu>::iterator it = cus.begin ();
+        it != cus.end (); ++it)
+      {
+       cu *cur = &*it;
+       if (last != NULL)
+         last->next = cur;
+       last = cur;
+      }
+    if (last != NULL)
+      last->next = NULL;
+  }
+
+  /* We used to check that all CUs have the same address size.  Now
+     that we validate address_size of each CU against the ELF header,
+     that's not necessary anymore.  */
+
+  check_global_die_references (!cus.empty () ? &cus.front () : NULL);
+
+  if (strings_coverage != NULL)
+    {
+      if (success)
+       {
+         struct hole_info info = {sec_str, mc_strings, strings->d_buf, 0};
+         strings_coverage->find_holes (0, strings->d_size, found_hole, &info);
+       }
+      delete strings_coverage;
+    }
+
+  // If we were unsuccessful, fail now.
+  if (!success)
+    throw check_base::failed ();
+
+  if (cus.size () > 0)
+    assert (cus.back ().next == NULL);
+}
+
+check_debug_info::~check_debug_info ()
+{
+}
+
+cu *
+check_debug_info::find_cu (::Dwarf_Off offset)
+{
+  for (std::vector<cu>::iterator it = cus.begin ();
+       it != cus.end (); ++it)
+    if (it->head->offset == offset)
+      return &*it;
+
+  return NULL;
+}
+
+checkdescriptor const *
+check_debug_info_refs::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_info_refs")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"This pass checks:\n"
+" - for outstanding unresolved references from .debug_info to .debug_line\n"
+" - that each CU has an associated aranges entry (that even if there is "
+"no .debug_aranges to begin with).\n"));
+  return &cd;
+}
+
+static reg<check_debug_info_refs> reg_debug_info_refs;
+
+check_debug_info_refs::check_debug_info_refs (checkstack &stack,
+                                             dwarflint &lint)
+  : _m_info (lint.check (stack, _m_info))
+  , _m_line (lint.toplev_check (stack, _m_line))
+  , _m_aranges (lint.toplev_check (stack, _m_aranges))
+{
+  // XXX if .debug_line is present and broken, we don't want to report
+  // every unsatisfied reference.  If .debug_line is absent and
+  // references are present, we want to diagnose that in one line.  If
+  // .debug_line is present and valid, then we want to check each
+  // reference separately.
+  for (std::vector<cu>::iterator it = _m_info->cus.begin ();
+       it != _m_info->cus.end (); ++it)
+    {
+      if (it->stmt_list.addr == (uint64_t)-1)
+       for (ref_record::const_iterator jt = it->decl_file_refs.begin ();
+            jt != it->decl_file_refs.end (); ++jt)
+         wr_error (jt->who)
+           << "references .debug_line table, but CU DIE lacks DW_AT_stmt_list."
+           << std::endl;
+      else if (_m_line == NULL
+              || !_m_line->has_line_table (it->stmt_list.addr))
+       wr_error (it->stmt_list.who)
+         << "unresolved reference to .debug_line table "
+         << pri::hex (it->stmt_list.addr) << '.' << std::endl;
+
+      if (_m_aranges != NULL && !it->has_arange)
+       wr_message (it->head->where,
+                   mc_impact_3 | mc_acc_suboptimal | mc_aranges | mc_info)
+         << "no aranges table is associated with this CU." << std::endl;
+    }
+}
diff --git a/dwarflint/check_debug_info.hh b/dwarflint/check_debug_info.hh
new file mode 100644 (file)
index 0000000..1377e10
--- /dev/null
@@ -0,0 +1,161 @@
+/* Low-level checking of .debug_info.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECK_DEBUG_INFO_HH
+#define DWARFLINT_CHECK_DEBUG_INFO_HH
+
+#include <libdw.h>
+#include "addr-record.hh"
+#include "elf_file_i.hh"
+#include "coverage.hh"
+#include "checks.hh"
+#include "check_debug_abbrev_i.hh"
+#include "check_debug_line_i.hh"
+#include "check_debug_aranges_i.hh"
+#include "sections_i.hh"
+#include "die_locus.hh"
+
+typedef ref_T<die_locus> ref;
+typedef ref_record_T<die_locus> ref_record;
+
+struct cu_head
+{
+  Dwarf_Off offset;
+  Dwarf_Off size;               // Size of this CU.
+  Dwarf_Off head_size;          // Size from begin to 1st byte of CU.
+  Dwarf_Off total_size;         // size + head_size
+
+  int offset_size;              // Offset size in this CU.
+  Dwarf_Off abbrev_offset;      // Abbreviation section that this CU uses.
+  int version;                  // CU version
+  int address_size;             // Address size in bytes on the target machine.
+
+  cu_locus where;
+
+  explicit cu_head (Dwarf_Off a_offset)
+    : offset (a_offset)
+    , size (0)
+    , head_size (0)
+    , total_size (0)
+    , offset_size (0)
+    , abbrev_offset (0)
+    , version (0)
+    , address_size (0)
+    , where (a_offset)
+  {}
+};
+
+struct cu
+{
+  struct cu *next;              // For compatibility with C level.
+                                // xxx will probably go away eventually
+  cu_head const *head;
+  uint64_t cudie_offset;
+  uint64_t low_pc;              // DW_AT_low_pc value of CU DIE, -1 if not present.
+  ::ref stmt_list;
+  addr_record die_addrs;        // Addresses where DIEs begin in this CU.
+  ref_record die_refs;          // DIE references into other CUs from this CU.
+  ref_record loc_refs;          // references into .debug_loc from this CU.
+  ref_record range_refs;        // references into .debug_ranges from this CU.
+  ref_record decl_file_refs;    // values of DW_AT_decl_file in this CU.
+  bool has_arange;              // Whether we saw arange section pointing at this CU.
+  bool has_pubnames;            // Likewise for pubnames.
+  bool has_pubtypes;            // Likewise for pubtypes.
+
+  cu ()
+    : next (NULL)
+    , head (NULL)
+    , cudie_offset (0)
+    , low_pc (0)
+    , has_arange (false)
+    , has_pubnames (false)
+    , has_pubtypes (false)
+  {}
+};
+
+/** The pass for reading basic .debug_info data -- the layout of
+    sections and their headers.  */
+class read_cu_headers
+  : public check<read_cu_headers>
+{
+  section<sec_info> *_m_sec_info;
+
+public:
+  static checkdescriptor const *descriptor ();
+  std::vector<cu_head> const cu_headers;
+  read_cu_headers (checkstack &stack, dwarflint &lint);
+};
+
+/** The pass for in-depth structural analysis of .debug_info.  */
+class check_debug_info
+  : public check<check_debug_info>
+{
+  section<sec_info> *_m_sec_info;
+  section<sec_str> *_m_sec_str;
+  elf_file const &_m_file;
+  check_debug_abbrev *_m_abbrevs;
+  read_cu_headers *_m_cu_headers;
+
+  // Abbreviation table with that offset had user(s) that failed
+  // validation.  Check for unused abbrevs should be skipped.
+  std::vector< ::Dwarf_Off> _m_abbr_skip;
+
+  // The check pass adds all low_pc/high_pc ranges loaded from DIE
+  // tree into this coverage structure.
+  coverage _m_cov;
+
+  // If, during the check, we find any rangeptr-class attributes, we
+  // set need_ranges to true.  cu_ranges pass then uses this as a hint
+  // whether to request .debug_ranges or not.
+  bool _m_need_ranges;
+
+  bool check_cu_structural (struct read_ctx *ctx,
+                           struct cu *const cu,
+                           Elf_Data *strings,
+                           struct coverage *strings_coverage,
+                           struct relocation_data *reloc);
+
+public:
+  static checkdescriptor const *descriptor ();
+
+  coverage const &cov () const { return _m_cov; }
+  bool need_ranges () const { return _m_need_ranges; }
+
+  // This is where the loaded CUs are stored.
+  std::vector<cu> cus;
+
+  check_debug_info (checkstack &stack, dwarflint &lint);
+  ~check_debug_info ();
+
+  cu *find_cu (::Dwarf_Off offset);
+};
+
+/** Check pending references that need other sections to be validated
+    first.  */
+class check_debug_info_refs
+  : public check<check_debug_info_refs>
+{
+  check_debug_info *_m_info;
+  check_debug_line *_m_line;
+  check_debug_aranges *_m_aranges;
+
+public:
+  static checkdescriptor const *descriptor ();
+  check_debug_info_refs (checkstack &stack, dwarflint &lint);
+};
+
+#endif//DWARFLINT_CHECK_DEBUG_INFO_HH
diff --git a/dwarflint/check_debug_info_i.hh b/dwarflint/check_debug_info_i.hh
new file mode 100644 (file)
index 0000000..ea388e3
--- /dev/null
@@ -0,0 +1,5 @@
+struct cu_head;
+struct cu;
+class read_cu_headers;
+class check_debug_info;
+class check_debug_info_refs;
diff --git a/dwarflint/check_debug_line.cc b/dwarflint/check_debug_line.cc
new file mode 100644 (file)
index 0000000..34047e2
--- /dev/null
@@ -0,0 +1,703 @@
+/* Low-level checking of .debug_line.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "check_debug_line.hh"
+#include "check_debug_info.hh"
+#include "sections.hh"
+#include "pri.hh"
+#include "checked_read.hh"
+#include "messages.hh"
+#include "misc.hh"
+
+#include <dwarf.h>
+#include "../libdw/known-dwarf.h"
+#include "../src/dwarfstrings.h"
+
+#include <sstream>
+
+checkdescriptor const *
+check_debug_line::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_line")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"Checks for low-level structure of .debug_line.  In addition it\n"
+"checks:\n"
+" - for normalized values of certain attributes (such as that "
+"default_is_stmt is 0 or 1, even though technically any non-zero "
+"value is allowed).\n"
+" - for valid setting of opcode base (i.e. non-zero) and any file"
+"indices\n"
+" - that all include directories and all files are used\n"
+" - that files with absolute paths don't refer to include directories,"
+"and otherwise that the directory reference is valid\n"
+" - that each used standard or extended opcode is known (note that this "
+"assumes that elfutils know about all opcodes used in practice.  Be "
+"sure to build against recent-enough version).\n"
+" - that the line number program is properly terminated with the "
+"DW_LNE_end_sequence instruction and that it contains at least one "
+"other instruction\n"
+" - that relocations are valid.  In ET_REL files that certain fields "
+"are relocated\n"
+"Furthermore, if .debug_info is valid, it is checked that each line "
+"table is used by some CU.\n"
+"TODOs:\n"
+" - overlaps in defined addresses are probably OK, one instruction can "
+"be derived from several statements.  But certain flags in table "
+"should be consistent in that case, namely is_stmt, basic_block, "
+"end_sequence, prologue_end, epilogue_begin, isa.\n"
+                  ));
+  return &cd;
+}
+
+static reg<check_debug_line> reg_debug_line;
+
+namespace
+{
+  struct include_directory_t
+  {
+    std::string name;
+    bool used;
+  };
+  typedef std::vector<include_directory_t> include_directories_t;
+
+  struct file_t
+  {
+    const char *name;
+    uint64_t dir_idx;
+    bool used;
+  };
+  typedef std::vector<file_t> files_t;
+
+  /* Directory index.  */
+  bool read_directory_index (include_directories_t &include_directories,
+                            files_t &files, read_ctx *ctx,
+                            const char *name, uint64_t *ptr,
+                            locus const &loc, bool &retval)
+  {
+    size_t nfile = files.size () + 1;
+    if (!checked_read_uleb128 (ctx, ptr,
+                              loc, "directory index"))
+      return false;
+
+    if (*name == '/' && *ptr != 0)
+      wr_message (loc, mc_impact_2 | mc_line | mc_header)
+       << "file #" << nfile
+       << " has absolute pathname, but refers to directory != 0."
+       << std::endl;
+
+    if (*ptr > include_directories.size ())
+      /* Not >=, dirs are indexed from 1.  */
+      {
+       wr_message (loc, mc_impact_4 | mc_line | mc_header)
+         << "file #" << nfile
+         << " refers to directory #" << *ptr
+         << ", which wasn't defined." << std::endl;
+
+       /* Consumer might choke on that.  */
+       retval = false;
+      }
+    else if (*ptr != 0)
+      include_directories[*ptr - 1].used = true;
+    return true;
+  }
+
+  bool
+  use_file (files_t &files, uint64_t file_idx,
+           locus const &loc, char const *msg = "")
+  {
+    if (file_idx == 0 || file_idx > files.size ())
+      {
+       wr_error (loc)
+         << msg << "invalid file index " << file_idx << '.'
+         << std::endl;
+       return false;
+      }
+    else
+      files[file_idx - 1].used = true;
+    return true;
+  }
+}
+
+namespace
+{
+  char const *
+  table_n ()
+  {
+    return "table";
+  }
+
+  typedef fixed_locus<sec_line, table_n,
+                     locus_simple_fmt::dec> line_table_locus;
+}
+
+check_debug_line::check_debug_line (checkstack &stack, dwarflint &lint)
+  : _m_sec (lint.check (stack, _m_sec))
+  , _m_info (lint.toplev_check (stack, _m_info))
+{
+  bool addr_64 = _m_sec->file.addr_64;
+  struct read_ctx ctx;
+  read_ctx_init (&ctx, _m_sec->sect.data, _m_sec->file.other_byte_order);
+
+  // For violations that the high-level might not handle.
+  bool success = true;
+
+  while (!read_ctx_eof (&ctx))
+    {
+      uint64_t set_offset = read_ctx_get_offset (&ctx);
+      line_table_locus where (set_offset);
+      _m_line_tables.insert ((Dwarf_Off)set_offset);
+      const unsigned char *set_begin = ctx.ptr;
+
+      /* Size.  */
+      uint32_t size32;
+      uint64_t size;
+      int offset_size;
+      if (!read_ctx_read_4ubyte (&ctx, &size32))
+       {
+         wr_error (where) << "can't read table length." << std::endl;
+         throw check_base::failed ();
+       }
+      if (!read_size_extra (&ctx, size32, &size, &offset_size, where))
+       throw check_base::failed ();
+
+      struct read_ctx sub_ctx;
+      const unsigned char *set_end = ctx.ptr + size;
+      if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
+       {
+       not_enough:
+         wr_error (where)
+           << pri::not_enough ("next unit") << '.' << std::endl;
+         throw check_base::failed ();
+       }
+      sub_ctx.ptr = ctx.ptr;
+      sub_ctx.begin = ctx.begin;
+
+      {
+      /* Version.  */
+      uint16_t version;
+      if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+       {
+         wr_error (where) << "can't read set version." << std::endl;
+       skip:
+         success = false;
+         goto next;
+       }
+      if (!supported_version (version, 2, where, 2, 3))
+       goto skip;
+
+      /* Header length.  */
+      uint64_t header_length;
+      if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &header_length))
+       {
+         wr_error (where) << "can't read attribute value." << std::endl;
+         goto skip;
+       }
+      const unsigned char *header_start = sub_ctx.ptr;
+
+      /* Minimum instruction length.  */
+      uint8_t minimum_i_length;
+      if (!read_ctx_read_ubyte (&sub_ctx, &minimum_i_length))
+       {
+         wr_error (where)
+           << "can't read minimum instruction length." << std::endl;
+         goto skip;
+       }
+
+      /* Default value of is_stmt.  */
+      uint8_t default_is_stmt;
+      if (!read_ctx_read_ubyte (&sub_ctx, &default_is_stmt))
+       {
+         wr_error (where) << "can't read default_is_stmt." << std::endl;
+         goto skip;
+       }
+      /* 7.21: The boolean values "true" and "false" used by the line
+        number information program are encoded as a single byte
+        containing the value 0 for "false," and a non-zero value for
+        "true."  [But give a notice if it's not 0 or 1.]  */
+      if (default_is_stmt != 0
+         && default_is_stmt != 1)
+       wr_message (where, mc_line | mc_impact_2 | mc_header)
+         << "default_is_stmt should be 0 or 1, not "
+         << default_is_stmt << '.' << std::endl;
+
+      /* Line base.  */
+      int8_t line_base;
+      if (!read_ctx_read_ubyte (&sub_ctx, (uint8_t *)&line_base))
+       {
+         wr_error (where) << "can't read line_base." << std::endl;
+         goto skip;
+       }
+
+      /* Line range.  */
+      uint8_t line_range;
+      if (!read_ctx_read_ubyte (&sub_ctx, &line_range))
+       {
+         wr_error (where) << "can't read line_range." << std::endl;
+         goto skip;
+       }
+
+      /* Opcode base.  */
+      uint8_t opcode_base;
+      if (!read_ctx_read_ubyte (&sub_ctx, &opcode_base))
+       {
+         wr_error (where) << "can't read opcode_base." << std::endl;
+         goto skip;
+       }
+
+      /* Standard opcode lengths.  */
+      if (opcode_base == 0)
+       {
+         wr_error (where) << "opcode base set to 0." << std::endl;
+         opcode_base = 1; // so that in following, our -1s don't underrun
+       }
+      uint8_t std_opc_lengths[opcode_base - 1]; /* -1, opcodes go from 1.  */
+      for (unsigned i = 0; i < (unsigned)(opcode_base - 1); ++i)
+       if (!read_ctx_read_ubyte (&sub_ctx, std_opc_lengths + i))
+         {
+           wr_error (where)
+             << "can't read length of standard opcode #" << i << '.'
+             << std::endl;
+           goto skip;
+         }
+
+      include_directories_t include_directories;
+      while (!read_ctx_eof (&sub_ctx))
+       {
+         const char *name = read_ctx_read_str (&sub_ctx);
+         if (name == NULL)
+           {
+             wr_error (where)
+               << "can't read name of include directory #"
+               << include_directories.size () + 1 // Numbered from 1.
+               << '.' << std::endl;
+             goto skip;
+           }
+         if (*name == 0)
+           break;
+
+         include_directories.push_back ((include_directory_t){name, false});
+       }
+
+      /* File names.  */
+      files_t files;
+      while (1)
+       {
+         const char *name = read_ctx_read_str (&sub_ctx);
+         if (name == NULL)
+           {
+             wr_error (where)
+               << "can't read name of file #"
+               << files.size () + 1 // Numbered from 1.
+               << '.' << std::endl;
+             goto skip;
+           }
+         if (*name == 0)
+           break;
+
+         uint64_t dir_idx;
+         if (!read_directory_index (include_directories, files,
+                                    &sub_ctx, name, &dir_idx, where, success))
+           goto skip;
+
+         /* Time of last modification.  */
+         uint64_t timestamp;
+         if (!checked_read_uleb128 (&sub_ctx, &timestamp,
+                                    where, "timestamp of file entry"))
+           goto skip;
+
+         /* Size of the file.  */
+         uint64_t file_size;
+         if (!checked_read_uleb128 (&sub_ctx, &file_size,
+                                    where, "file size of file entry"))
+           goto skip;
+
+         files.push_back ((struct file_t){name, dir_idx, false});
+       }
+
+      /* Now that we have table of filenames, validate DW_AT_decl_file
+        references.  We don't include filenames defined through
+        DW_LNE_define_file in consideration.  */
+
+      if (_m_info != NULL)
+       {
+         bool found = false;
+         for (std::vector<cu>::const_iterator it = _m_info->cus.begin ();
+              it != _m_info->cus.end (); ++it)
+           if (it->stmt_list.addr == set_offset)
+             {
+               found = true;
+               for (ref_record::const_iterator
+                      jt = it->decl_file_refs.begin ();
+                    jt != it->decl_file_refs.end (); ++jt)
+                 if (!use_file (files, jt->addr, jt->who))
+                   success = false;
+             }
+         if (!found)
+           wr_message (where, mc_line)
+             << "no CU uses this line table." << std::endl;
+       }
+
+      const unsigned char *program_start = header_start + header_length;
+      if (header_length > (uint64_t)(sub_ctx.end - header_start)
+         || sub_ctx.ptr > program_start)
+       {
+         wr_error (where)
+           << "header claims that it has a size of " << header_length
+           << ", but in fact it has a size of "
+           << (sub_ctx.ptr - program_start + header_length)
+           << '.' << std::endl;
+
+         /* Assume that the header lies, and what follows is in
+            fact line number program.  */
+         success = false;
+       }
+      else if (sub_ctx.ptr < program_start)
+       {
+         /* Skip the rest of the header.  */
+         uint64_t off_start, off_end;
+         if (read_check_zero_padding (&sub_ctx, &off_start, &off_end))
+           wr_message_padding_0 (mc_line | mc_header, section_locus (sec_line),
+                                 off_start, off_end);
+         else
+           wr_message_padding_n0
+             (mc_line | mc_header, section_locus (sec_line),
+              off_start, program_start - sub_ctx.begin);
+         sub_ctx.ptr = program_start;
+       }
+
+      bool terminated = false;
+      bool first_file = true;
+      bool seen_opcode = false;
+      while (!read_ctx_eof (&sub_ctx))
+       {
+         section_locus op_where (sec_line, read_ctx_get_offset (&sub_ctx));
+         uint8_t opcode;
+         if (!read_ctx_read_ubyte (&sub_ctx, &opcode))
+           {
+             wr_error (op_where) << "can't read opcode." << std::endl;
+             goto skip;
+           }
+
+         unsigned operands = 0;
+         uint8_t extended = 0;
+         switch (opcode)
+           {
+             /* Extended opcodes.  */
+           case 0:
+             {
+               uint64_t skip_len;
+               if (!checked_read_uleb128 (&sub_ctx, &skip_len, op_where,
+                                          "length of extended opcode"))
+                 goto skip;
+               if (!read_ctx_need_data (&sub_ctx, skip_len))
+                 {
+                   wr_error (op_where)
+                     << "not enough data to read an opcode of length "
+                     << skip_len << '.' << std::endl;
+                   goto skip;
+                 }
+
+               const unsigned char *next = sub_ctx.ptr + skip_len;
+               if (!read_ctx_read_ubyte (&sub_ctx, &extended))
+                 {
+                   wr_error (op_where)
+                     << "can't read extended opcode." << std::endl;
+                   goto skip;
+                 }
+
+               bool handled = true;
+               switch (extended)
+                 {
+                 case DW_LNE_end_sequence:
+                   terminated = true;
+                   break;
+
+                 case DW_LNE_set_address:
+                   {
+                     uint64_t ctx_offset = read_ctx_get_offset (&sub_ctx);
+                     uint64_t addr;
+                     if (!read_ctx_read_offset (&sub_ctx, addr_64, &addr))
+                       {
+                         wr_error (op_where)
+                           << "can't read operand of DW_LNE_set_address."
+                           << std::endl;
+                         goto skip;
+                       }
+
+                     struct relocation *rel;
+                     if ((rel = relocation_next (&_m_sec->sect.rel, ctx_offset,
+                                                 op_where, skip_mismatched)))
+                       relocate_one (&_m_sec->file, &_m_sec->sect.rel, rel,
+                                     addr_64 ? 8 : 4, &addr, op_where,
+                                     rel_target::rel_address, NULL);
+                     else if (_m_sec->file.ehdr.e_type == ET_REL)
+                       {
+                         wr_message (op_where,
+                                     mc_impact_2 | mc_line | mc_reloc)
+                           << pri::lacks_relocation ("DW_LNE_set_address")
+                           << '.' << std::endl;
+
+                         // Don't do the addr checking in this case.
+                         break;
+                       }
+
+                     if (addr == 0)
+                       wr_message (op_where, mc_line | mc_impact_1)
+                         << "DW_LNE_set_address with zero operand."
+                         << std::endl;
+                     break;
+                   }
+
+                 case DW_LNE_set_discriminator:
+                   {
+                     /* XXX Is there anything interesting we should
+                        check here?  */
+                     uint64_t disc;
+                     if (!checked_read_uleb128 (&sub_ctx, &disc, op_where,
+                                                "set_discriminator operand"))
+                       goto skip;
+
+                     /* The discriminator is reset to zero on any
+                        sequence change.  So setting to zero is never
+                        necessary.  */
+                     if (disc == 0)
+                       wr_message (op_where, mc_line | mc_impact_1)
+                         << "DW_LNE_set_discriminator with zero operand."
+                         << std::endl;
+                     break;
+                   }
+
+                 case DW_LNE_define_file:
+                   {
+                     const char *name;
+                     if ((name = read_ctx_read_str (&sub_ctx)) == NULL)
+                       {
+                         wr_error (op_where)
+                           << "can't read filename operand of DW_LNE_define_file."
+                           << std::endl;
+                         goto skip;
+                       }
+                     uint64_t dir_idx;
+                     if (!read_directory_index (include_directories,
+                                                files, &sub_ctx, name,
+                                                &dir_idx, op_where, success))
+                       goto skip;
+                     files.push_back
+                       ((struct file_t){name, dir_idx, false});
+                     operands = 2; /* Skip mtime & size of the file.  */
+                   }
+
+                   /* See if we know about any other standard opcodes.  */
+                 default:
+                   handled = false;
+                   switch (extended)
+                     {
+#define ONE_KNOWN_DW_LNE(NAME, CODE) case CODE: break;
+                       ALL_KNOWN_DW_LNE
+#undef ONE_KNOWN_DW_LNE
+                     default:
+                       /* No we don't, emit a warning.  */
+                       wr_message (op_where, mc_impact_2 | mc_line)
+                         << "unknown extended opcode 0x"
+                         << std::hex << +extended << std::dec
+                         << '.' << std::endl;
+                     };
+                 };
+
+               if (sub_ctx.ptr > next)
+                 {
+                   wr_error (op_where)
+                     << "opcode claims that it has a size of " << skip_len
+                     << ", but in fact it has a size of "
+                     << (skip_len + (next - sub_ctx.ptr)) << '.' << std::endl;
+                   success = false;
+                 }
+               else if (sub_ctx.ptr < next)
+                 {
+                   uint64_t off_start, off_end;
+                   if (handled)
+                     {
+                       if (read_check_zero_padding (&sub_ctx,
+                                                    &off_start, &off_end))
+                         wr_message_padding_0
+                           (mc_line, section_locus (sec_line),
+                            off_start, off_end);
+                       else
+                         wr_message_padding_n0
+                           (mc_line, section_locus (sec_line),
+                            off_start, next - sub_ctx.begin);
+                     }
+                   sub_ctx.ptr = next;
+                 }
+               break;
+             }
+
+             /* Standard opcodes that need validation or have
+                non-ULEB operands.  */
+           case DW_LNS_advance_line:
+             {
+               int64_t line_delta;
+               if (!checked_read_sleb128 (&sub_ctx, &line_delta, op_where,
+                                          "DW_LNS_advance_line operand"))
+                 goto skip;
+             }
+             break;
+
+           case DW_LNS_fixed_advance_pc:
+             {
+               uint16_t a;
+               if (!read_ctx_read_2ubyte (&sub_ctx, &a))
+                 {
+                   wr_error (op_where)
+                     << "can't read operand of DW_LNS_fixed_advance_pc."
+                     << std::endl;
+                   goto skip;
+                 }
+               break;
+             }
+
+           case DW_LNS_set_file:
+             {
+               uint64_t file_idx;
+               if (!checked_read_uleb128 (&sub_ctx, &file_idx, op_where,
+                                          "DW_LNS_set_file operand"))
+                 goto skip;
+               if (!use_file (files, file_idx, op_where, "DW_LNS_set_file: "))
+                 success = false;
+               first_file = false;
+             }
+             break;
+
+           case DW_LNS_set_isa:
+             // XXX is it possible to validate this?
+             operands = 1;
+             break;
+
+             /* All the other opcodes.  */
+           default:
+             if (opcode < opcode_base)
+               operands = std_opc_lengths[opcode - 1];
+
+             switch (opcode)
+               {
+#define ONE_KNOWN_DW_LNS(NAME, CODE) case CODE: break;
+                 ALL_KNOWN_DW_LNS
+#undef ONE_KNOWN_DW_LNS
+
+               default:
+                 if (opcode < opcode_base)
+                   wr_message (op_where, mc_impact_2 | mc_line)
+                     << "unknown standard opcode 0x"
+                     << std::hex << +opcode << std::dec
+                     << '.' << std::endl;
+               };
+           };
+
+         for (unsigned i = 0; i < operands; ++i)
+           {
+             uint64_t operand;
+             char buf[128];
+             if (opcode != 0)
+               sprintf (buf, "operand #%d of DW_LNS_%s",
+                        i, dwarf_line_standard_opcode_string (opcode));
+             else
+               sprintf (buf, "operand #%d of DW_LNE_%s",
+                        i, dwarf_line_extended_opcode_string (extended));
+             if (!checked_read_uleb128 (&sub_ctx, &operand, op_where, buf))
+               goto skip;
+           }
+
+         if (first_file)
+           {
+             if (!use_file (files, 1, op_where,
+                            "initial value of `file' register: "))
+               success = false;
+             first_file = false;
+           }
+
+         if (opcode != 0 || extended != DW_LNE_end_sequence)
+           seen_opcode = true;
+       }
+
+      for (size_t i = 0; i < include_directories.size (); ++i)
+       if (!include_directories[i].used)
+         wr_message (where,
+                     mc_impact_3 | mc_acc_bloat | mc_line | mc_header)
+           << "the include #" << i + 1
+           << " `" << include_directories[i].name
+           << "' is not used." << std::endl;
+
+      // We can't do full analysis unless we know which DIEs refer to
+      // files.
+      if (_m_info != NULL)
+       {
+         bool useful = false;
+
+         for (size_t i = 0; i < files.size (); ++i)
+           if (!files[i].used)
+             wr_message (where,
+                         mc_impact_3 | mc_acc_bloat | mc_line | mc_header)
+               << "the file #" << i + 1
+               << " `" << files[i].name << "' is not used." << std::endl;
+           else
+             useful = true;
+
+         if (!seen_opcode && !useful)
+           wr_message (where, mc_line | mc_acc_bloat | mc_impact_3)
+             << "empty line number program and no references from .debug_info."
+             << std::endl;
+       }
+
+      if (!terminated && seen_opcode)
+       wr_error (where)
+         << "sequence of opcodes not terminated with DW_LNE_end_sequence."
+         << std::endl;
+      else if (sub_ctx.ptr != sub_ctx.end)
+       {
+         uint64_t off_start, off_end;
+         if (read_check_zero_padding (&sub_ctx, &off_start, &off_end))
+           wr_message_padding_0
+             (mc_line, section_locus (sec_line), off_start, off_end);
+         else
+           wr_message_padding_n0 (mc_line, section_locus (sec_line),
+                                  off_start, sub_ctx.end - sub_ctx.begin);
+       }
+      }
+
+    next:
+      if (!read_ctx_skip (&ctx, size))
+       goto not_enough;
+    }
+
+  if (success)
+    relocation_skip_rest (&_m_sec->sect.rel, section_locus (_m_sec->sect.id));
+  else
+    throw check_base::failed ();
+}
+
+bool
+check_debug_line::has_line_table (Dwarf_Off off) const
+{
+  return _m_line_tables.find (off) != _m_line_tables.end ();
+}
diff --git a/dwarflint/check_debug_line.hh b/dwarflint/check_debug_line.hh
new file mode 100644 (file)
index 0000000..41fde29
--- /dev/null
@@ -0,0 +1,44 @@
+/* Low-level checking of .debug_line
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECK_DEBUG_LINE_HH
+#define DWARFLINT_CHECK_DEBUG_LINE_HH
+
+#include "check_debug_info_i.hh"
+#include "sections_i.hh"
+#include "checks.hh"
+
+#include "../libdw/libdw.h"
+#include <set>
+
+class check_debug_line
+  : public check<check_debug_line>
+{
+  section<sec_line> *_m_sec;
+  check_debug_info *_m_info;
+  std::set<Dwarf_Off> _m_line_tables;
+
+public:
+  static checkdescriptor const *descriptor ();
+  check_debug_line (checkstack &stack, dwarflint &lint);
+
+  std::set<Dwarf_Off> const &line_tables () const { return _m_line_tables; }
+
+  bool has_line_table (Dwarf_Off off) const;
+};
+
+#endif//DWARFLINT_CHECK_DEBUG_LINE_HH
diff --git a/dwarflint/check_debug_line_i.hh b/dwarflint/check_debug_line_i.hh
new file mode 100644 (file)
index 0000000..5d9a1ff
--- /dev/null
@@ -0,0 +1 @@
+class check_debug_line;
diff --git a/dwarflint/check_debug_loc_range.cc b/dwarflint/check_debug_loc_range.cc
new file mode 100644 (file)
index 0000000..5f28622
--- /dev/null
@@ -0,0 +1,1041 @@
+/* Routines related to .debug_loc and .debug_range.
+
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+// xxx drop as soon as not necessary
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cassert>
+#include <sstream>
+#include <algorithm>
+
+#include "../libdw/c++/dwarf"
+#include "../src/dwarf-opcodes.h"
+
+#include "elf_file.hh"
+#include "check_debug_loc_range.hh"
+#include "check_debug_info.hh"
+#include "sections.hh"
+#include "checked_read.hh"
+#include "pri.hh"
+#include "misc.hh"
+
+bool do_range_coverage = false; // currently no option
+
+global_opt<void_option>
+  opt_show_refs("\
+When validating .debug_loc and .debug_ranges, display information about \
+the DIE referring to the entry in consideration", "ref");
+
+std::string
+loc_range_locus::format (bool brief) const
+{
+  std::stringstream ss;
+  if (!brief)
+    ss << section_name[_m_sec] << ": ";
+
+  if (_m_sec == sec_loc)
+    ss << "loclist";
+  else
+    ss << "rangelist";
+
+  if (_m_offset != (Dwarf_Off)-1)
+    ss << " 0x" << std::hex << _m_offset;
+
+  if (opt_show_refs)
+    ss << ", ref. by " << _m_refby.format (true);
+
+  return ss.str ();
+}
+
+checkdescriptor const *
+check_debug_ranges::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_ranges")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"Checks for low-level structure of .debug_ranges.  In addition it "
+"checks:\n"
+" - for overlapping and dangling references from .debug_info\n"
+" - that base address is set and that it actually changes the address\n"
+" - that ranges have a positive size\n"
+" - that there are no unreferenced holes in the section\n"
+" - that relocations are valid.  In ET_REL files that certain fields "
+"are relocated\n"
+" - neither or both of range start and range end are expected to be "
+"relocated.  It's expected that they are both relocated against the "
+"same section.\n"));
+  return &cd;
+}
+
+static reg<check_debug_ranges> reg_debug_ranges;
+
+checkdescriptor const *
+check_debug_loc::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_loc")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"Checks for low-level structure of .debug_loc.  In addition it "
+"makes the same checks as .debug_ranges.  For location expressions "
+"it further checks:\n"
+" - that DW_OP_bra and DW_OP_skip argument is non-zero and doesn't "
+"escape the expression.  In addition it is required that the jump "
+"ends on another instruction, not arbitrarily in the middle of the "
+"byte stream, even if that position happened to be interpretable as "
+"another well-defined instruction stream.\n"
+" - on 32-bit machines it rejects DW_OP_const8u and DW_OP_const8s\n"
+" - on 32-bit machines it checks that ULEB128-encoded arguments aren't "
+"quantities that don't fit into 32 bits\n"));
+  return &cd;
+}
+
+static reg<check_debug_loc> reg_debug_loc;
+
+namespace
+{
+  bool
+  coverage_map_init (struct coverage_map *coverage_map,
+                    struct elf_file *elf,
+                    Elf64_Xword mask,
+                    Elf64_Xword warn_mask,
+                    bool allow_overlap)
+  {
+    assert (coverage_map != NULL);
+    assert (elf != NULL);
+
+    coverage_map->elf = elf;
+    coverage_map->allow_overlap = allow_overlap;
+
+    for (size_t i = 1; i < elf->size; ++i)
+      {
+       struct sec *sec = elf->sec + i;
+
+       bool normal = (sec->shdr.sh_flags & mask) == mask;
+       bool warn = (sec->shdr.sh_flags & warn_mask) == warn_mask;
+       if (normal || warn)
+         coverage_map->scos
+           .push_back (section_coverage (sec, !normal));
+      }
+
+    return true;
+  }
+
+  struct coverage_map *
+  coverage_map_alloc_XA (struct elf_file *elf, bool allow_overlap)
+  {
+    coverage_map *ret = new coverage_map ();
+    if (!coverage_map_init (ret, elf,
+                           SHF_EXECINSTR | SHF_ALLOC,
+                           SHF_ALLOC,
+                           allow_overlap))
+      {
+       delete ret;
+       return NULL;
+      }
+    return ret;
+  }
+
+  struct hole_env
+  {
+    locus const &loc;
+    uint64_t address;
+    uint64_t end;
+  };
+
+  bool
+  range_hole (uint64_t h_start, uint64_t h_length, void *xenv)
+  {
+    hole_env *env = (hole_env *)xenv;
+    char buf[128], buf2[128];
+    assert (h_length != 0);
+    wr_error (&env->loc,
+             ": portion %s of the range %s "
+             "doesn't fall into any ALLOC section.\n",
+             range_fmt (buf, sizeof buf,
+                        h_start + env->address, h_start + env->address + h_length),
+             range_fmt (buf2, sizeof buf2, env->address, env->end));
+    return true;
+  }
+
+  struct coverage_map_hole_info
+  {
+    struct elf_file *elf;
+    struct hole_info info;
+  };
+
+  /* begin is inclusive, end is exclusive. */
+  bool
+  coverage_map_found_hole (uint64_t begin, uint64_t end,
+                          struct section_coverage *sco, void *user)
+  {
+    struct coverage_map_hole_info *info = (struct coverage_map_hole_info *)user;
+
+    const char *scnname = sco->sec->name;
+
+    struct sec *sec = sco->sec;
+    GElf_Xword align = sec->shdr.sh_addralign;
+
+    /* We don't expect some sections to be covered.  But if they
+       are at least partially covered, we expect the same
+       coverage criteria as for .text.  */
+    if (!sco->hit
+       && ((sco->sec->shdr.sh_flags & SHF_EXECINSTR) == 0
+           || strcmp (scnname, ".init") == 0
+           || strcmp (scnname, ".fini") == 0
+           || strcmp (scnname, ".plt") == 0))
+      return true;
+
+    /* For REL files, don't print addresses mangled by our layout.  */
+    uint64_t base = info->elf->ehdr.e_type == ET_REL ? 0 : sco->sec->shdr.sh_addr;
+
+    /* If the hole is filled with NUL bytes, don't report it.  But if we
+       get stripped debuginfo file, the data may not be available.  In
+       that case don't report the hole, if it seems to be alignment
+       padding.  */
+    if (sco->sec->data->d_buf != NULL)
+      {
+       bool zeroes = true;
+       for (uint64_t j = begin; j < end; ++j)
+         if (((char *)sco->sec->data->d_buf)[j] != 0)
+           {
+             zeroes = false;
+             break;
+           }
+       if (zeroes)
+         return true;
+      }
+    else if (necessary_alignment (base + begin, end - begin, align))
+      return true;
+
+    char buf[128];
+    wr_message (section_locus (info->info.section),
+               info->info.category | mc_acc_suboptimal | mc_impact_4)
+      << "addresses " << range_fmt (buf, sizeof buf, begin + base, end + base)
+      << " of section " << scnname << " are not covered.\n";
+    return true;
+  }
+
+  struct wrap_cb_arg
+  {
+    bool (*cb) (uint64_t begin, uint64_t end,
+               struct section_coverage *, void *);
+    section_coverage *sco;
+    void *user;
+  };
+
+  bool
+  unwrap_cb (uint64_t h_start, uint64_t h_length, void *user)
+  {
+    wrap_cb_arg *arg = (wrap_cb_arg *)user;
+    return (arg->cb) (h_start, h_start + h_length, arg->sco, arg->user);
+  }
+
+  bool
+  coverage_map_find_holes (struct coverage_map *coverage_map,
+                          bool (*cb) (uint64_t begin, uint64_t end,
+                                      struct section_coverage *, void *),
+                          void *user)
+  {
+    for (size_t i = 0; i < coverage_map->size; ++i)
+      {
+       section_coverage *sco = &coverage_map->scos[i];
+       wrap_cb_arg arg = {cb, sco, user};
+       if (!sco->cov.find_holes (0, sco->sec->shdr.sh_size, unwrap_cb, &arg))
+         return false;
+      }
+
+    return true;
+  }
+
+  void
+  coverage_map_add (struct coverage_map *coverage_map,
+                   uint64_t address,
+                   uint64_t length,
+                   locus const &loc,
+                   enum message_category cat)
+  {
+    bool found = false;
+    bool crosses_boundary = false;
+    bool overlap = false;
+    uint64_t end = address + length;
+    char buf[128]; // for messages
+
+    /* This is for analyzing how much of the current range falls into
+       sections in coverage map.  Whatever is left uncovered doesn't
+       fall anywhere and is reported.  */
+    coverage range_cov;
+
+    for (size_t i = 0; i < coverage_map->size; ++i)
+      {
+       struct section_coverage *sco = &coverage_map->scos[i];
+       GElf_Shdr *shdr = &sco->sec->shdr;
+       struct coverage *cov = &sco->cov;
+
+       Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size;
+       if (end <= shdr->sh_addr || address >= s_end)
+         /* no overlap */
+         continue;
+
+       if (found && !crosses_boundary)
+         {
+           /* While probably not an error, it's very suspicious.  */
+           wr_message (cat | mc_impact_2, &loc,
+                       ": the range %s crosses section boundaries.\n",
+                       range_fmt (buf, sizeof buf, address, end));
+           crosses_boundary = true;
+         }
+
+       found = true;
+
+       if (length == 0)
+         /* Empty range.  That means no actual coverage, and we can
+            also be sure that there are no more sections that this one
+            falls into.  */
+         break;
+
+       uint64_t cov_begin
+         = address < shdr->sh_addr ? 0 : address - shdr->sh_addr;
+       uint64_t cov_end
+         = end < s_end ? end - shdr->sh_addr : shdr->sh_size;
+       assert (cov_begin < cov_end);
+
+       uint64_t r_delta = shdr->sh_addr - address;
+       uint64_t r_cov_begin = cov_begin + r_delta;
+       uint64_t r_cov_end = cov_end + r_delta;
+
+       if (!overlap && !coverage_map->allow_overlap
+           && cov->is_overlap (cov_begin, cov_end - cov_begin))
+         {
+           /* Not a show stopper, this shouldn't derail high-level.  */
+           wr_message (loc, cat | mc_aranges | mc_impact_2 | mc_error)
+             << "the range " << range_fmt (buf, sizeof buf, address, end)
+             << " overlaps with another one." << std::endl;
+           overlap = true;
+         }
+
+       if (sco->warn)
+         wr_message (cat | mc_impact_2, &loc,
+                     ": the range %s covers section %s.\n",
+                     range_fmt (buf, sizeof buf, address, end), sco->sec->name);
+
+       /* Section coverage... */
+       cov->add (cov_begin, cov_end - cov_begin);
+       sco->hit = true;
+
+       /* And range coverage... */
+       range_cov.add (r_cov_begin, r_cov_end - r_cov_begin);
+      }
+
+    if (!found)
+      /* Not a show stopper.  */
+      wr_error (&loc,
+               ": couldn't find a section that the range %s covers.\n",
+               range_fmt (buf, sizeof buf, address, end));
+    else if (length > 0)
+      {
+       hole_env env = {loc, address, end};
+       range_cov.find_holes (0, length, range_hole, &env);
+      }
+  }
+
+  bool
+  check_loc_or_range_ref (dwarf_version const *ver,
+                         struct elf_file *file,
+                         const struct read_ctx *parent_ctx,
+                         struct cu *cu,
+                         struct sec *sec,
+                         struct coverage *coverage,
+                         struct coverage_map *coverage_map,
+                         struct coverage *pc_coverage,
+                         uint64_t addr,
+                         locus const &loc,
+                         enum message_category cat)
+  {
+    char buf[128]; // messages
+
+    assert (sec->id == sec_loc || sec->id == sec_ranges);
+    assert (cat == mc_loc || cat == mc_ranges);
+    assert ((sec->id == sec_loc) == (cat == mc_loc));
+    assert (coverage != NULL);
+
+    struct read_ctx ctx;
+    read_ctx_init (&ctx, parent_ctx->data, file->other_byte_order);
+    if (!read_ctx_skip (&ctx, addr))
+      {
+       wr_error (&loc, ": invalid reference outside the section "
+                 "%#" PRIx64 ", size only %#tx.\n",
+                 addr, ctx.end - ctx.begin);
+       return false;
+      }
+
+    bool retval = true;
+    bool contains_locations = sec->id == sec_loc;
+
+    if (coverage->is_covered (addr, 1))
+      {
+       wr_error (&loc, ": reference to %#" PRIx64
+                 " points into another location or range list.\n", addr);
+       retval = false;
+      }
+
+    uint64_t escape = cu->head->address_size == 8
+      ? (uint64_t)-1 : (uint64_t)(uint32_t)-1;
+
+    bool overlap = false;
+    uint64_t base = cu->low_pc;
+    while (!read_ctx_eof (&ctx))
+      {
+       uint64_t offset = read_ctx_get_offset (&ctx);
+       loc_range_locus where (sec->id, loc, offset);
+
+#define HAVE_OVERLAP                                           \
+       do {                                                    \
+         wr_error (&where, ": range definitions overlap.\n");  \
+         retval = false;                                       \
+         overlap = true;                                       \
+       } while (0)
+
+       /* begin address */
+       uint64_t begin_addr;
+       uint64_t begin_off = read_ctx_get_offset (&ctx);
+       GElf_Sym begin_symbol_mem, *begin_symbol = &begin_symbol_mem;
+       bool begin_relocated = false;
+       if (!overlap
+           && coverage->is_overlap (begin_off, cu->head->address_size))
+         HAVE_OVERLAP;
+
+       if (!read_ctx_read_offset (&ctx, cu->head->address_size == 8, &begin_addr))
+         {
+           wr_error (&where, ": can't read address range beginning.\n");
+           return false;
+         }
+
+       struct relocation *rel;
+       if ((rel = relocation_next (&sec->rel, begin_off,
+                                   where, skip_mismatched)))
+         {
+           begin_relocated = true;
+           relocate_one (file, &sec->rel, rel, cu->head->address_size,
+                         &begin_addr, where, rel_target::rel_value,
+                         &begin_symbol);
+         }
+
+       /* end address */
+       uint64_t end_addr;
+       uint64_t end_off = read_ctx_get_offset (&ctx);
+       GElf_Sym end_symbol_mem, *end_symbol = &end_symbol_mem;
+       bool end_relocated = false;
+       if (!overlap
+           && coverage->is_overlap (end_off, cu->head->address_size))
+         HAVE_OVERLAP;
+
+       if (!read_ctx_read_offset (&ctx, cu->head->address_size == 8,
+                                  &end_addr))
+         {
+           wr_error (&where, ": can't read address range ending.\n");
+           return false;
+         }
+
+       if ((rel = relocation_next (&sec->rel, end_off,
+                                   where, skip_mismatched)))
+         {
+           end_relocated = true;
+           relocate_one (file, &sec->rel, rel, cu->head->address_size,
+                         &end_addr, where, rel_target::rel_value, &end_symbol);
+           if (begin_addr != escape)
+             {
+               if (!begin_relocated)
+                 wr_message (cat | mc_impact_2 | mc_reloc, &where,
+                             ": end of address range is relocated, but the beginning wasn't.\n");
+               else
+                 check_range_relocations (where, cat, file,
+                                          begin_symbol, end_symbol,
+                                          "begin and end address");
+             }
+         }
+       else if (begin_relocated)
+         wr_message (cat | mc_impact_2 | mc_reloc, &where,
+                     ": end of address range is not relocated, but the beginning was.\n");
+
+       bool done = false;
+       if (begin_addr == 0 && end_addr == 0 && !begin_relocated && !end_relocated)
+         done = true;
+       else if (begin_addr != escape)
+         {
+           if (base == (uint64_t)-1)
+             {
+               wr_error (&where,
+                         ": address range with no base address set: %s.\n",
+                         range_fmt (buf, sizeof buf, begin_addr, end_addr));
+               /* This is not something that would derail high-level,
+                  so carry on.  */
+             }
+
+           if (end_addr < begin_addr)
+             wr_message (cat | mc_error, &where, ": has negative range %s.\n",
+                         range_fmt (buf, sizeof buf, begin_addr, end_addr));
+           else if (begin_addr == end_addr)
+             /* 2.6.6: A location list entry [...] whose beginning
+                and ending addresses are equal has no effect.  */
+             wr_message (cat | mc_acc_bloat | mc_impact_3, &where,
+                         ": entry covers no range.\n");
+           /* Skip coverage analysis if we have errors or have no base
+              (or just don't do coverage analysis at all).  */
+           else if (base < (uint64_t)-2 && retval
+                    && (coverage_map != NULL || pc_coverage != NULL))
+             {
+               uint64_t address = begin_addr + base;
+               uint64_t length = end_addr - begin_addr;
+               if (coverage_map != NULL)
+                 coverage_map_add (coverage_map, address, length, where, cat);
+               if (pc_coverage != NULL)
+                 pc_coverage->add (address, length);
+             }
+
+           if (contains_locations)
+             {
+               /* location expression length */
+               uint16_t len;
+               if (!overlap
+                   && coverage->is_overlap (read_ctx_get_offset (&ctx), 2))
+                 HAVE_OVERLAP;
+
+               if (!read_ctx_read_2ubyte (&ctx, &len))
+                 {
+                   wr_error (where)
+                     << "can't read length of location expression."
+                     << std::endl;
+                   return false;
+                 }
+
+               /* location expression itself */
+               uint64_t expr_start = read_ctx_get_offset (&ctx);
+               if (!check_location_expression
+                   (ver, *file, &ctx, cu, expr_start, &sec->rel, len, where))
+                 return false;
+               uint64_t expr_end = read_ctx_get_offset (&ctx);
+               if (!overlap
+                   && coverage->is_overlap (expr_start, expr_end - expr_start))
+                 HAVE_OVERLAP;
+
+               if (!read_ctx_skip (&ctx, len))
+                 {
+                   /* "can't happen" */
+                   wr_error (&where, PRI_NOT_ENOUGH, "location expression");
+                   return false;
+                 }
+             }
+         }
+       else
+         {
+           if (end_addr == base)
+             wr_message (cat | mc_acc_bloat | mc_impact_3, &where,
+                         ": base address selection doesn't change base address"
+                         " (%#" PRIx64 ").\n", base);
+           else
+             base = end_addr;
+         }
+#undef HAVE_OVERLAP
+
+       coverage->add (offset, read_ctx_get_offset (&ctx) - offset);
+       if (done)
+         break;
+      }
+
+    return retval;
+  }
+
+  struct ref_cu
+  {
+    ::ref ref;
+    ::cu *cu;
+
+    bool
+    operator < (ref_cu const& other) const
+    {
+      return ref.addr < other.ref.addr;
+    }
+  };
+
+  bool
+  check_loc_or_range_structural (struct elf_file *file,
+                                struct sec *sec,
+                                struct cu *cu_chain,
+                                struct coverage *pc_coverage)
+  {
+    assert (sec->id == sec_loc || sec->id == sec_ranges);
+    assert (cu_chain != NULL);
+
+    struct read_ctx ctx;
+    read_ctx_init (&ctx, sec->data, file->other_byte_order);
+
+    bool retval = true;
+
+    /* For .debug_ranges, we optionally do ranges vs. ELF sections
+       coverage analysis.  */
+    // xxx this is a candidate for a separate check
+    struct coverage_map *coverage_map = NULL;
+    if (do_range_coverage && sec->id == sec_ranges
+       && (coverage_map
+           = coverage_map_alloc_XA (file, sec->id == sec_loc)) == NULL)
+      {
+       wr_error (section_locus (sec->id))
+         << "couldn't read ELF, skipping coverage analysis." << std::endl;
+       retval = false;
+      }
+
+    /* Overlap discovery.  */
+    struct coverage coverage;
+
+    enum message_category cat = sec->id == sec_loc ? mc_loc : mc_ranges;
+
+    {
+    /* Relocation checking in the followings assumes that all the
+       references are organized in monotonously increasing order.  That
+       doesn't have to be the case.  So merge all the references into
+       one sorted array.  */
+    typedef std::vector<ref_cu> ref_cu_vect;
+    ref_cu_vect refs;
+    for (struct cu *cu = cu_chain; cu != NULL; cu = cu->next)
+      {
+       ref_record *rec
+         = sec->id == sec_loc ? &cu->loc_refs : &cu->range_refs;
+       for (ref_record::const_iterator it = rec->begin ();
+            it != rec->end (); ++it)
+         {
+           ref_cu ref = {*it, cu};
+           refs.push_back (ref);
+         }
+      }
+    std::sort (refs.begin (), refs.end ());
+
+    uint64_t last_off = 0;
+    for (ref_cu_vect::const_iterator it = refs.begin ();
+        it != refs.end (); ++it)
+      {
+       uint64_t off = it->ref.addr;
+       if (it != refs.begin ())
+         {
+           if (off == last_off)
+             continue;
+           relocation_skip (&sec->rel, off, section_locus (sec->id),
+                            skip_unref);
+         }
+
+       // xxx right now this is just so that we can ver->get_form
+       // down the road, which is just a result of the way
+       // dwarf-opcodes encode operator operand types.  But in the
+       // future, we'd like versions to support also queries for
+       // operators and their operands, so keep it.
+       dwarf_version const *ver
+         = dwarf_version::get (it->cu->head->version);
+
+       /* XXX We pass cu_coverage down for all ranges.  That means all
+          ranges get recorded, not only those belonging to CUs.
+          Perhaps that's undesirable.  */
+       if (!check_loc_or_range_ref (ver, file, &ctx, it->cu, sec,
+                                    &coverage, coverage_map, pc_coverage,
+                                    off, it->ref.who, cat))
+         retval = false;
+       last_off = off;
+      }
+    }
+
+    if (retval)
+      {
+       relocation_skip_rest (&sec->rel, section_locus (sec->id));
+
+       /* We check that all CUs have the same address size when building
+          the CU chain.  So just take the address size of the first CU in
+          chain.  */
+       struct hole_info hi = {
+         sec->id, cat, ctx.data->d_buf, cu_chain->head->address_size
+       };
+       coverage.find_holes (0, ctx.data->d_size, found_hole, &hi);
+
+       if (coverage_map)
+         {
+           struct coverage_map_hole_info cmhi = {
+             coverage_map->elf, {sec->id, cat, NULL, 0}
+           };
+           coverage_map_find_holes (coverage_map, &coverage_map_found_hole,
+                                    &cmhi);
+         }
+      }
+
+    delete coverage_map;
+
+    return retval;
+  }
+}
+
+section_coverage::section_coverage (struct sec *a_sec, bool a_warn)
+  : sec (a_sec)
+  , hit (false)
+  , warn (a_warn)
+{
+  assert (a_sec);
+}
+
+check_debug_ranges::check_debug_ranges (checkstack &stack, dwarflint &lint)
+  : _m_sec_ranges (lint.check (stack, _m_sec_ranges))
+  , _m_info (lint.check (stack, _m_info))
+{
+  memset (&_m_cov, 0, sizeof (_m_cov));
+  if (!::check_loc_or_range_structural (&_m_sec_ranges->file,
+                                       &_m_sec_ranges->sect,
+                                       &_m_info->cus.front (),
+                                       &_m_cov))
+    throw check_base::failed ();
+}
+
+check_debug_loc::check_debug_loc (checkstack &stack, dwarflint &lint)
+  : _m_sec_loc (lint.check (stack, _m_sec_loc))
+  , _m_info (lint.check (stack, _m_info))
+{
+  if (!::check_loc_or_range_structural (&_m_sec_loc->file,
+                                       &_m_sec_loc->sect,
+                                       &_m_info->cus.front (),
+                                       NULL))
+    throw check_base::failed ();
+}
+
+namespace
+{
+  /* Operands are passed back as attribute forms.  In particular,
+     DW_FORM_dataX for X-byte operands, DW_FORM_[us]data for
+     ULEB128/SLEB128 operands, and DW_FORM_addr/DW_FORM_ref_addr
+     for 32b/64b operands.
+     If the opcode takes no operands, 0 is passed.
+
+     Return value is false if we couldn't determine (i.e. invalid
+     opcode).
+  */
+
+  bool
+  get_location_opcode_operands (dwarf_version const *ver,
+                               uint8_t opcode,
+                               form const **f1p,
+                               form const **f2p)
+  {
+    int op1, op2;
+    switch (opcode)
+      {
+#define DW_OP_2(OPCODE, OP1, OP2)                              \
+       case OPCODE: op1 = OP1; op2 = OP2; break;
+#define DW_OP_1(OPCODE, OP1) DW_OP_2(OPCODE, OP1, 0)
+#define DW_OP_0(OPCODE) DW_OP_2(OPCODE, 0, 0)
+
+       DW_OP_OPERANDS
+
+#undef DEF_DW_OP_2
+#undef DEF_DW_OP_1
+#undef DEF_DW_OP_0
+      default:
+       return false;
+      };
+
+#define RETV(OP,P)             \
+    if (OP != 0)               \
+      {                                \
+       form const *f = NULL;   \
+       f = ver->get_form (OP); \
+       if (f == NULL)          \
+         return false;         \
+       *P = f;                 \
+      }                                \
+    else                       \
+      *P = NULL;
+
+    RETV (op1, f1p);
+    RETV (op2, f2p);
+    return true;
+  }
+
+  static rel_target
+  reloc_target_loc (uint8_t opcode)
+  {
+    switch (opcode)
+      {
+      case DW_OP_call2:
+      case DW_OP_call4:
+       return sec_info;
+
+      case DW_OP_addr:
+       return rel_target::rel_address;
+
+      case DW_OP_call_ref:
+       assert (!"Can't handle call_ref!");
+      };
+
+    std::cout << "XXX don't know how to handle opcode="
+             << elfutils::dwarf::ops::name (opcode) << std::endl;
+
+    return rel_target::rel_value;
+  }
+
+  bool
+  op_read_form (struct elf_file const &file,
+               struct read_ctx *ctx,
+               struct cu *cu,
+               uint64_t init_off,
+               struct relocation_data *reloc,
+               int opcode,
+               form const *form,
+               uint64_t *valuep,
+               char const *str,
+               locus const &where)
+  {
+    if (form == NULL)
+      return true;
+
+    uint64_t off = read_ctx_get_offset (ctx) + init_off;
+
+    storage_class_t storclass = form->storage_class ();
+    assert (storclass != sc_string);
+    if (!read_generic_value (ctx, form->width (cu->head), storclass,
+                            where, valuep, NULL))
+      {
+       wr_error (where)
+         << "opcode \"" << elfutils::dwarf::ops::name (opcode)
+         << "\": can't read " << str << " (form \""
+         << *form << "\")." << std::endl;
+       return false;
+      }
+
+    /* For non-block forms, allow relocation of the datum.  For block
+       form, allow relocation of block contents, but not the
+       block length).  */
+
+    struct relocation *rel;
+    if ((rel = relocation_next (reloc, off,
+                               where, skip_mismatched)))
+      {
+       if (storclass != sc_block)
+         relocate_one (&file, reloc, rel,
+                       cu->head->address_size, valuep, where,
+                       reloc_target_loc (opcode), NULL);
+       else
+         wr_error (where) << "relocation relocates a length field.\n";
+      }
+    if (storclass == sc_block)
+      {
+       uint64_t off_block_end = read_ctx_get_offset (ctx) + init_off - 1;
+       relocation_next (reloc, off_block_end, where, skip_ok);
+      }
+
+    return true;
+  }
+}
+
+class locexpr_locus
+  : public locus
+{
+  uint64_t _m_offset;
+  locus const *_m_context;
+
+public:
+  explicit locexpr_locus (uint64_t offset, locus const *context)
+    : _m_offset (offset)
+    , _m_context (context)
+  {}
+
+  std::string
+  format (bool) const
+  {
+    std::stringstream ss;
+    ss << _m_context
+       << " (location expression offset 0x" << std::hex << _m_offset << ")";
+    return ss.str ();
+  }
+};
+
+bool
+check_location_expression (dwarf_version const *ver,
+                          elf_file const &file,
+                          struct read_ctx *parent_ctx,
+                          struct cu *cu,
+                          uint64_t init_off,
+                          struct relocation_data *reloc,
+                          size_t length,
+                          locus const &loc)
+{
+  struct read_ctx ctx;
+  if (!read_ctx_init_sub (&ctx, parent_ctx, parent_ctx->ptr,
+                         parent_ctx->ptr + length))
+    {
+      wr_error (&loc, PRI_NOT_ENOUGH, "location expression");
+      return false;
+    }
+
+  typedef ref_T<locexpr_locus> locexpr_ref;
+  typedef ref_record_T<locexpr_locus> locexpr_ref_record;
+  locexpr_ref_record oprefs;
+  addr_record opaddrs;
+
+  while (!read_ctx_eof (&ctx))
+    {
+      uint64_t opcode_off = read_ctx_get_offset (&ctx) + init_off;
+      locexpr_locus where (opcode_off, &loc);
+      opaddrs.add (opcode_off);
+
+      uint8_t opcode;
+      if (!read_ctx_read_ubyte (&ctx, &opcode))
+       {
+         wr_error (&where, ": can't read opcode.\n");
+         break;
+       }
+
+      form const *form1 = NULL;
+      form const *form2 = NULL;
+      if (!get_location_opcode_operands (ver, opcode, &form1, &form2))
+       {
+         wr_error (where)
+           << "can't decode opcode \""
+           << elfutils::dwarf::ops::name (opcode) << "\"." << std::endl;
+         break;
+       }
+
+      uint64_t value1, value2;
+      if (!op_read_form (file, &ctx, cu, init_off, reloc,
+                        opcode, form1, &value1, "1st operand", where)
+         || !op_read_form (file, &ctx, cu, init_off, reloc,
+                           opcode, form2, &value2, "2st operand", where))
+       goto out;
+
+      switch (opcode)
+       {
+       case DW_OP_bra:
+       case DW_OP_skip:
+         {
+           int16_t skip = (uint16_t)value1;
+
+           if (skip == 0)
+             wr_message (where, mc_loc | mc_acc_bloat | mc_impact_3)
+               << elfutils::dwarf::ops::name (opcode)
+               << " with skip 0." << std::endl;
+           else if (skip > 0 && !read_ctx_need_data (&ctx, (size_t)skip))
+             wr_error (where)
+               << elfutils::dwarf::ops::name (opcode)
+               << " branches out of location expression." << std::endl;
+           /* Compare with the offset after the two-byte skip value.  */
+           else if (skip < 0 && ((uint64_t)-skip) > read_ctx_get_offset (&ctx))
+             wr_error (where)
+               << elfutils::dwarf::ops::name (opcode)
+               << " branches before the beginning of location expression."
+               << std::endl;
+           else
+             {
+               uint64_t off_after = read_ctx_get_offset (&ctx) + init_off;
+               oprefs.push_back (locexpr_ref (off_after + skip, where));
+             }
+
+           break;
+         }
+
+       case DW_OP_const8u:
+       case DW_OP_const8s:
+         if (cu->head->address_size == 4)
+           wr_error (where)
+             << elfutils::dwarf::ops::name (opcode) << " on 32-bit machine."
+             << std::endl;
+         break;
+
+       default:
+         if (cu->head->address_size == 4
+             && (opcode == DW_OP_constu
+                 || opcode == DW_OP_consts
+                 || opcode == DW_OP_deref_size
+                 || opcode == DW_OP_plus_uconst)
+             && (value1 > (uint64_t)(uint32_t)-1))
+           wr_message (where, mc_loc | mc_acc_bloat | mc_impact_3)
+             << elfutils::dwarf::ops::name (opcode)
+             << " with operand " << pri::hex (value1)
+             << " on a 32-bit machine." << std::endl;
+       }
+    }
+
+ out:
+  for (locexpr_ref_record::const_iterator it = oprefs.begin ();
+       it != oprefs.end (); ++it)
+    if (!opaddrs.has_addr (it->addr))
+      wr_error (it->who) << "unresolved reference to opcode at "
+                        << pri::hex (it->addr) << ".\n";
+
+  return true;
+}
+
+bool
+found_hole (uint64_t start, uint64_t length, void *data)
+{
+  struct hole_info *info = (struct hole_info *)data;
+  bool all_zeroes = true;
+  for (uint64_t i = start; i < start + length; ++i)
+    if (((char*)info->data)[i] != 0)
+      {
+       all_zeroes = false;
+       break;
+      }
+
+  uint64_t end = start + length;
+  if (all_zeroes)
+    {
+      /* Zero padding is valid, if it aligns on the bounds of
+        info->align bytes, and is not excessive.  */
+      if (info->align == 0 || info->align == 1
+         || length > info->align     // excessive
+         || end % info->align != 0   // doesn't actually align
+         || start % info->align == 0)// was already aligned
+       wr_message_padding_0 (info->category, section_locus (info->section),
+                             start, end);
+    }
+  else
+    /* XXX: This actually lies when the unreferenced portion is
+       composed of sequences of zeroes and non-zeroes.  */
+    wr_message_padding_n0 (info->category, section_locus (info->section),
+                          start, end);
+
+  return true;
+}
+
+void
+check_range_relocations (locus const &loc,
+                        enum message_category cat,
+                        struct elf_file const *file,
+                        GElf_Sym *begin_symbol,
+                        GElf_Sym *end_symbol,
+                        const char *description)
+{
+  if (begin_symbol != NULL
+      && end_symbol != NULL
+      && begin_symbol->st_shndx != end_symbol->st_shndx)
+    wr_message (cat | mc_impact_2 | mc_reloc, &loc,
+               ": %s relocated against different sections (%s and %s).\n",
+               description,
+               file->sec[begin_symbol->st_shndx].name,
+               file->sec[end_symbol->st_shndx].name);
+}
diff --git a/dwarflint/check_debug_loc_range.hh b/dwarflint/check_debug_loc_range.hh
new file mode 100644 (file)
index 0000000..4fbc6f7
--- /dev/null
@@ -0,0 +1,112 @@
+/* Low-level checking of .debug_loc and .debug_range.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "checks.hh"
+#include "sections_i.hh"
+#include "check_debug_info_i.hh"
+#include "messages.hh"
+#include "coverage.hh"
+#include "dwarf_version_i.hh"
+
+class loc_range_locus
+  : public locus
+{
+  locus const &_m_refby;
+  Dwarf_Off _m_offset;
+  section_id _m_sec;
+
+public:
+  loc_range_locus (section_id sec, locus const &refby, Dwarf_Off offset = -1)
+    : _m_refby (refby)
+    , _m_offset (offset)
+    , _m_sec ((assert (sec == sec_loc || sec == sec_ranges), sec))
+  {}
+
+  std::string format (bool brief) const;
+};
+
+struct section_coverage
+{
+  struct sec *sec;
+  struct coverage cov;
+  bool hit; /* true if COV is not pristine.  */
+  bool warn; /* dwarflint should emit a warning if a coverage appears
+               in this section */
+  section_coverage (struct sec *a_sec, bool a_warn);
+};
+
+struct coverage_map
+{
+  struct elf_file *elf;
+  std::vector<section_coverage> scos;
+  size_t size;
+  size_t alloc;
+  bool allow_overlap;
+};
+
+class check_debug_ranges
+  : public check<check_debug_ranges>
+{
+  section<sec_ranges> *_m_sec_ranges;
+  check_debug_info *_m_info;
+  coverage _m_cov;
+
+public:
+  static checkdescriptor const *descriptor ();
+
+  coverage const &cov () const { return _m_cov; }
+  check_debug_ranges (checkstack &stack, dwarflint &lint);
+};
+
+class check_debug_loc
+  : public check<check_debug_loc>
+{
+  section<sec_loc> *_m_sec_loc;
+  check_debug_info *_m_info;
+
+public:
+  static checkdescriptor const *descriptor ();
+  check_debug_loc (checkstack &stack, dwarflint &lint);
+};
+
+struct hole_info
+{
+  enum section_id section;
+  enum message_category category;
+  void *data;
+  unsigned align;
+};
+
+/* DATA has to be a pointer to an instance of struct hole_info.
+   DATA->data has to point at d_buf of section in question.  */
+bool found_hole (uint64_t start, uint64_t length, void *data);
+
+bool check_location_expression (dwarf_version const *ver,
+                               elf_file const &file,
+                               struct read_ctx *parent_ctx,
+                               struct cu *cu,
+                               uint64_t init_off,
+                               struct relocation_data *reloc,
+                               size_t length,
+                               locus const &loc);
+
+void check_range_relocations (locus const &loc,
+                             enum message_category cat,
+                             struct elf_file const *file,
+                             GElf_Sym *begin_symbol,
+                             GElf_Sym *end_symbol,
+                             const char *description);
diff --git a/dwarflint/check_debug_loc_range_i.hh b/dwarflint/check_debug_loc_range_i.hh
new file mode 100644 (file)
index 0000000..a5926e1
--- /dev/null
@@ -0,0 +1,3 @@
+class check_debug_ranges;
+class check_debug_loc;
+struct hole_info;
diff --git a/dwarflint/check_debug_pub.cc b/dwarflint/check_debug_pub.cc
new file mode 100644 (file)
index 0000000..8ac3928
--- /dev/null
@@ -0,0 +1,260 @@
+/* Low-level checking of .debug_pub*.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "check_debug_pub.hh"
+#include "check_debug_info.hh"
+#include "sections.hh"
+#include "pri.hh"
+#include "checked_read.hh"
+#include "messages.hh"
+#include "misc.hh"
+
+namespace
+{
+  bool check_pub_structural (elf_file const &file, sec &sect,
+                            check_debug_info *cus);
+}
+
+template<section_id sec_id>
+check_debug_pub<sec_id>::check_debug_pub (checkstack &stack, dwarflint &lint)
+  : _m_sec (lint.check (stack, _m_sec))
+  , _m_file (_m_sec->file)
+  , _m_cus (lint.toplev_check (stack, _m_cus))
+{
+  check_pub_structural (_m_file, _m_sec->sect, _m_cus);
+}
+
+
+checkdescriptor const *
+check_debug_pubnames::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_pubnames")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"Checks for low-level structure of .debug_pubnames.  In addition it "
+"checks:\n"
+" - for garbage inside padding\n"
+" - that relocations are valid.  In ET_REL files that certain fields "
+"are relocated\n"
+"Furthermore, if .debug_info is valid, it is checked:\n"
+" - that references point to actual CUs and DIEs\n"
+" - that there's only one pub section per CU\n"));
+  return &cd;
+}
+
+template check_debug_pub<sec_pubnames>::check_debug_pub (checkstack &stack,
+                                                        dwarflint &lint);
+
+static reg<check_debug_pubnames> reg_debug_pubnames;
+
+checkdescriptor const *
+check_debug_pubtypes::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("check_debug_pubtypes")
+     .groups ("@low")
+     .schedule (false)
+     .description (
+"Checks for low-level structure of .debug_pubtypes.  In addition it "
+"makes the same checks as check_debug_pubnames.\n"));
+  return &cd;
+}
+
+template check_debug_pub<sec_pubtypes>::check_debug_pub (checkstack &stack,
+                                                        dwarflint &lint);
+
+static reg<check_debug_pubtypes> reg_debug_pubtypes;
+
+namespace
+{
+  bool
+  check_pub_structural (elf_file const &file, sec &sect,
+                       check_debug_info *cus)
+  {
+    struct read_ctx ctx;
+    read_ctx_init (&ctx, sect.data, file.other_byte_order);
+    bool retval = true;
+
+    while (!read_ctx_eof (&ctx))
+      {
+       enum section_id sid = sect.id;
+       section_locus where (sid, read_ctx_get_offset (&ctx));
+       const unsigned char *set_begin = ctx.ptr;
+
+       /* Size.  */
+       uint32_t size32;
+       uint64_t size;
+       int offset_size;
+       if (!read_ctx_read_4ubyte (&ctx, &size32))
+         {
+           wr_error (&where, ": can't read table length.\n");
+           return false;
+         }
+       if (!read_size_extra (&ctx, size32, &size, &offset_size, where))
+         return false;
+
+       {
+         struct read_ctx sub_ctx;
+         const unsigned char *set_end = ctx.ptr + size;
+         if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
+           goto not_enough;
+         sub_ctx.ptr = ctx.ptr;
+
+         /* Version.  */
+         uint16_t version;
+         if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+           {
+             wr_error (&where, ": can't read set version.\n");
+             retval = false;
+             goto next;
+           }
+         if (!supported_version (version, 1, where, 2))
+           {
+             retval = false;
+             goto next;
+           }
+
+         /* CU offset.  */
+         uint64_t cu_offset;  /* Offset of related CU.  */
+         {
+         uint64_t ctx_offset = sub_ctx.ptr - ctx.begin;
+         if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_offset))
+           {
+             wr_error (&where, ": can't read debug info offset.\n");
+             retval = false;
+             goto next;
+           }
+
+         struct relocation *rel;
+         if ((rel = relocation_next (&sect.rel, ctx_offset,
+                                     where, skip_mismatched)))
+           relocate_one (&file, &sect.rel, rel, offset_size,
+                         &cu_offset, where, sec_info, NULL);
+         else if (file.ehdr.e_type == ET_REL)
+           wr_message (mc_impact_2 | mc_pubtables | mc_reloc | mc_header, &where,
+                       PRI_LACK_RELOCATION, "debug info offset");
+         }
+
+         struct cu *cu = NULL;
+         if (cus != NULL && (cu = cus->find_cu (cu_offset)) == NULL)
+           wr_error (where)
+             << "unresolved reference to " << pri::CU (cu_offset)
+             << '.' << std::endl;
+         // xxx this can be checked even without CU
+         if (cu != NULL)
+           {
+             //where.ref = &cu->head->where;
+             bool *has = sect.id == sec_pubnames
+               ? &cu->has_pubnames : &cu->has_pubtypes;
+             if (*has)
+               wr_message (mc_impact_2 | mc_pubtables | mc_header, &where,
+                           ": there has already been section for this CU.\n");
+             else
+               *has = true;
+           }
+
+         /* Covered length.  */
+         uint64_t cu_len;
+         if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_len))
+           {
+             wr_error (&where, ": can't read covered length.\n");
+             retval = false;
+             goto next;
+           }
+         if (cu != NULL && cu_len != cu->head->total_size)
+           {
+             wr_error (where)
+               << "the table covers length " << cu_len << " but CU has length "
+               << cu->head->total_size << '.' << std::endl;
+             retval = false;
+             goto next;
+           }
+
+         /* Records... */
+         while (!read_ctx_eof (&sub_ctx))
+           {
+             section_locus rec_where (sect.id, sub_ctx.ptr - ctx.begin);
+
+             uint64_t offset;
+             if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &offset))
+               {
+                 wr_error (&rec_where, ": can't read offset field.\n");
+                 retval = false;
+                 goto next;
+               }
+             if (offset == 0)
+               break;
+
+             if (cu != NULL
+                 && !cu->die_addrs.has_addr (offset + cu->head->offset))
+               {
+                 wr_error (rec_where)
+                   << "unresolved reference to " << pri::DIE (offset)
+                   << '.' << std::endl;
+                 retval = false;
+                 goto next;
+               }
+
+             // xxx read_ctx_read_str???
+             uint8_t c;
+             do
+               if (!read_ctx_read_ubyte (&sub_ctx, &c))
+                 {
+                   wr_error (&rec_where, ": can't read symbol name.\n");
+                   retval = false;
+                   goto next;
+                 }
+             while (c);
+           }
+
+         if (sub_ctx.ptr != sub_ctx.end)
+           {
+             uint64_t off_start, off_end;
+             if (read_check_zero_padding (&sub_ctx, &off_start, &off_end))
+               wr_message_padding_0 (mc_pubtables, section_locus (sect.id),
+                                     off_start, off_end);
+             else
+               {
+                 wr_message_padding_n0 (mc_pubtables | mc_error,
+                                        section_locus (sect.id),
+                                        off_start, off_start + size);
+                 retval = false;
+               }
+           }
+       }
+
+      next:
+       if (read_ctx_skip (&ctx, size))
+         continue;
+
+      not_enough:
+       wr_error (&where, PRI_NOT_ENOUGH, "next set");
+       return false;
+      }
+
+    if (retval)
+      relocation_skip_rest (&sect.rel, section_locus (sect.id));
+
+    return retval;
+  }
+}
diff --git a/dwarflint/check_debug_pub.hh b/dwarflint/check_debug_pub.hh
new file mode 100644 (file)
index 0000000..5ee6bc6
--- /dev/null
@@ -0,0 +1,61 @@
+/* Low-level checking of .debug_pub*.
+   Copyright (C) 2009 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECK_DEBUG_PUB_HH
+#define DWARFLINT_CHECK_DEBUG_PUB_HH
+
+#include "sections_i.hh"
+#include "check_debug_info_i.hh"
+#include "checks.hh"
+#include "elf_file_i.hh"
+
+template<section_id sec_id>
+class check_debug_pub
+  : public check<check_debug_pub<sec_id> >
+{
+protected:
+  typedef section<sec_id> section_t;
+  section_t *_m_sec;
+  elf_file const &_m_file;
+  check_debug_info *_m_cus;
+
+public:
+  // instantiated in .cc for each subclass
+  check_debug_pub (checkstack &stack, dwarflint &lint);
+};
+
+struct check_debug_pubnames
+  : public check_debug_pub<sec_pubnames>
+{
+  static checkdescriptor const *descriptor ();
+
+  check_debug_pubnames (checkstack &stack, dwarflint &lint)
+    : check_debug_pub<sec_pubnames> (stack, lint)
+  {}
+};
+
+struct check_debug_pubtypes
+  : public check_debug_pub<sec_pubtypes>
+{
+  static checkdescriptor const *descriptor ();
+
+  check_debug_pubtypes (checkstack &stack, dwarflint &lint)
+    : check_debug_pub<sec_pubtypes> (stack, lint)
+  {}
+};
+
+#endif//DWARFLINT_CHECK_DEBUG_PUB_HH
diff --git a/dwarflint/check_die_decl_call.cc b/dwarflint/check_die_decl_call.cc
new file mode 100644 (file)
index 0000000..079d99e
--- /dev/null
@@ -0,0 +1,105 @@
+/* Check that decl or call file, line, column come in pairs.
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "check_die_tree.hh"
+#include "pri.hh"
+#include "messages.hh"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_die_decl_call
+    : public die_check
+  {
+  public:
+    static checkdescriptor const *descriptor ()
+    {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_die_decl_call")
+        .description ("Check that each decl or call attribute come in"
+                      " file/line and column/line pairs.\n"));
+      return &cd;
+    }
+
+    check_die_decl_call (highlevel_check_i *, checkstack &, dwarflint &)
+    {
+      // No state stored for this check.
+    }
+
+    virtual void
+    die (all_dies_iterator<dwarf> const &it)
+    {
+      dwarf::debug_info_entry const &entry = *it;
+      dwarf::debug_info_entry::attributes_type attrs = entry.attributes ();
+
+      // Make sure decl column and line, and file and line are paired.
+      dwarf::debug_info_entry::attributes_type::const_iterator
+       decl_column = attrs.find (DW_AT_decl_column);
+      dwarf::debug_info_entry::attributes_type::const_iterator
+       decl_line = attrs.find (DW_AT_decl_line);
+      dwarf::debug_info_entry::attributes_type::const_iterator
+       decl_file = attrs.find (DW_AT_decl_file);
+
+      if (decl_column != attrs.end () && decl_line == attrs.end ())
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " has decl_column, but NOT decl_line" << std::endl;
+
+      if (decl_line != attrs.end () && decl_file == attrs.end ())
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " has decl_line, but NOT decl_file" << std::endl;
+
+      if (decl_file != attrs.end () && decl_line == attrs.end ())
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " has decl_file, but NOT decl_line" << std::endl;
+
+      // Same for call variants.
+      dwarf::debug_info_entry::attributes_type::const_iterator
+       call_column = attrs.find (DW_AT_call_column);
+      dwarf::debug_info_entry::attributes_type::const_iterator
+       call_line = attrs.find (DW_AT_call_line);
+      dwarf::debug_info_entry::attributes_type::const_iterator
+       call_file = attrs.find (DW_AT_call_file);
+
+      if (call_column != attrs.end () && call_line == attrs.end ())
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " has call_column, but NOT call_line" << std::endl;
+
+      if (call_line != attrs.end () && call_file == attrs.end ())
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " has call_line, but NOT call_file" << std::endl;
+
+      if (call_file != attrs.end () && call_line == attrs.end ())
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " has call_file, but NOT call_line" << std::endl;
+    }
+  };
+
+  reg_die_check<check_die_decl_call> reg;
+}
diff --git a/dwarflint/check_die_line_info.cc b/dwarflint/check_die_line_info.cc
new file mode 100644 (file)
index 0000000..359976f
--- /dev/null
@@ -0,0 +1,136 @@
+/* Check that every block that has an address is also in line info table.
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "check_die_tree.hh"
+#include "pri.hh"
+#include "messages.hh"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_die_line_info
+    : public die_check
+  {
+  public:
+    static checkdescriptor const *descriptor ()
+    {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_die_line_info")
+        .description ("Check that each code block start address "
+                      "is also mentioned in the line table.\n"));
+      return &cd;
+    }
+
+    check_die_line_info (highlevel_check_i *, checkstack &, dwarflint &)
+    {
+      // No state stored for this check.
+    }
+
+    static bool is_code_block (dwarf::debug_info_entry const &entry)
+    {
+      int tag = entry.tag ();
+      switch (tag)
+       {
+       case DW_TAG_subprogram:
+       case DW_TAG_inlined_subroutine:
+       case DW_TAG_entry_point:
+       case DW_TAG_lexical_block:
+       case DW_TAG_label:
+       case DW_TAG_with_stmt:
+       case DW_TAG_try_block:
+       case DW_TAG_catch_block:
+         return true;
+
+       default:
+         return false;
+       }
+    }
+
+    static void check_die_pc (dwarf::debug_info_entry const &entry,
+                             dwarf::attribute const &pc,
+                             Dwarf_Addr addr)
+    {
+      dwarf::compile_unit cu = entry.compile_unit ();
+      dwarf::line_info_table line_info = cu.line_info ();
+      dwarf::line_table lines = line_info.lines ();
+      dwarf::line_table::const_iterator l = lines.find (addr);
+
+      bool found = false;
+      while (l != lines.end ())
+       {
+         dwarf::line_entry line = *l;
+         if (line.address () < addr)
+           {
+             l++;
+             continue;
+           }
+         else if (line.address () > addr)
+           {
+             // Ran past it...
+             break;
+           }
+
+         found = true;
+         break;
+       }
+
+      if (! found)
+       wr_message (die_locus (entry), mc_impact_3 | mc_acc_suboptimal)
+         .id (descriptor ())
+         << elfutils::dwarf::tags::name (entry.tag ())
+         << " " << dwarf::attributes::name (pc.first) << "=0x"
+         << std::hex << addr << std::dec
+         << ", NOT found in line table." << std::endl;
+    }
+
+    virtual void
+    die (all_dies_iterator<dwarf> const &it)
+    {
+      dwarf::debug_info_entry const &entry = *it;
+      if (is_code_block (entry))
+       {
+         dwarf::debug_info_entry::attributes_type attrs = entry.attributes ();
+
+         dwarf::debug_info_entry::attributes_type::const_iterator
+           entry_pc = attrs.find (DW_AT_entry_pc);
+         if (entry_pc != attrs.end ())
+           check_die_pc (entry, *entry_pc, (*entry_pc).second.address ());
+
+         dwarf::debug_info_entry::attributes_type::const_iterator
+           low_pc = attrs.find (DW_AT_low_pc);
+         if (low_pc != attrs.end ())
+           check_die_pc (entry, *low_pc, (*low_pc).second.address ());
+
+         dwarf::debug_info_entry::attributes_type::const_iterator
+           at_ranges = attrs.find (DW_AT_ranges);
+         if (at_ranges != attrs.end ())
+           {
+             dwarf::ranges ranges = entry.ranges ();
+             dwarf::ranges::const_iterator r = ranges.begin ();
+             while (r != ranges.end ())
+               {
+                 check_die_pc (entry, *at_ranges, (*r).first);
+                 r++;
+               }
+           }
+       }
+    }
+  };
+
+  reg_die_check<check_die_line_info> reg;
+}
diff --git a/dwarflint/check_die_tree.cc b/dwarflint/check_die_tree.cc
new file mode 100644 (file)
index 0000000..c9f0fb2
--- /dev/null
@@ -0,0 +1,147 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "messages.hh"
+#include "highlevel_check.hh"
+#include "check_die_tree.hh"
+
+using namespace elfutils;
+
+namespace
+{
+  class die_check_registrar
+    : public check_registrar_T<die_check_item>
+  {
+  public:
+    friend class dwarflint;
+    void run (checkstack &stack, dwarflint &lint);
+
+    static die_check_registrar *
+    inst ()
+    {
+      static die_check_registrar inst;
+      return &inst;
+    }
+  };
+}
+
+void
+check_die_tree::register_check (die_check_item *check)
+{
+  die_check_registrar::inst ()->push_back (check);
+}
+
+class die_check_context
+  : protected std::vector<die_check *>
+{
+  typedef std::vector<die_check *> _super_t;
+  checkdescriptor const *_m_cd;
+
+public:
+  die_check_context (highlevel_check_i *check,
+                    checkdescriptor const *cd,
+                    dwarflint &lint,
+                    die_check_registrar const &registrar)
+    : _m_cd (cd)
+  {
+    // For per-DIE runs, we are only interested in limited context:
+    // the main iteration check, and the per-DIE check.  This should
+    // be enough to decide whether to run the per-DIE check or not.
+    // We cannot use the original stack as a criterion, because the
+    // original check that tricked us into running is here, and the
+    // logic in should_check would then assume that we need to run
+    // everything.
+    checkstack stack;
+    stack.push_back (cd);
+
+    for (die_check_registrar::const_iterator it = registrar.begin ();
+        it != registrar.end (); ++it)
+      {
+       stack.push_back ((*it)->descriptor ());
+       popper p (stack);
+       if (lint.rules ().should_check (stack))
+         push_back ((*it)->create (check, stack, lint));
+      }
+  }
+
+  void
+  error (all_dies_iterator<dwarf> const &a_d_it,
+        char const *reason = NULL)
+  {
+    std::string r;
+    if (reason)
+      {
+       r += ": ";
+       r += reason;
+      }
+
+    wr_error (die_locus (*a_d_it))
+      << "A check failed: " << (_m_cd->name () ?: "(nil)")
+      << r << std::endl;
+  }
+
+  void
+  die (all_dies_iterator<dwarf> const &a_d_it)
+  {
+    for (iterator it = begin (); it != end (); ++it)
+    again:
+      try
+       {
+         (*it)->die (a_d_it);
+       }
+      catch (check_base::unscheduled &e)
+       {
+         // Turn the check off.
+         size_t pos = it - begin ();
+         delete *it;
+         erase (it);
+         it = begin () + pos;
+         if (it == end ())
+           break;
+         goto again;
+       }
+      catch (check_base::failed &e)
+       {
+         // The check was supposed to emit an error message.
+       }
+      catch (std::exception &e)
+       {
+         error (a_d_it, e.what ());
+       }
+      catch (...)
+       {
+         error (a_d_it);
+       }
+  }
+
+  ~die_check_context ()
+  {
+    for (iterator it = begin (); it != end (); ++it)
+      delete *it;
+  }
+};
+
+check_die_tree::check_die_tree (checkstack &stack, dwarflint &lint)
+  : highlevel_check<check_die_tree> (stack, lint)
+{
+  die_check_context ctx (this, descriptor (), lint,
+                        *die_check_registrar::inst ());
+
+  for (all_dies_iterator<dwarf> it = all_dies_iterator<dwarf> (dw);
+       it != all_dies_iterator<dwarf> (); ++it)
+    ctx.die (it);
+}
diff --git a/dwarflint/check_die_tree.hh b/dwarflint/check_die_tree.hh
new file mode 100644 (file)
index 0000000..f896780
--- /dev/null
@@ -0,0 +1,109 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CHECK_DIE_TREE_H_
+#define _CHECK_DIE_TREE_H_
+
+#include "all-dies-it.hh"
+#include "highlevel_check.hh"
+#include "check_die_tree_i.hh"
+
+#include <c++/dwarf>
+
+struct die_check_item
+{
+  virtual checkdescriptor const *descriptor () const = 0;
+  virtual ~die_check_item () {}
+  virtual die_check *create (highlevel_check_i *check,
+                            checkstack &stack, dwarflint &lint) = 0;
+};
+
+/// Top-level check that iterates over all DIEs in a file and
+/// dispatches per-DIE checks on each one.  Per-DIE checks are written
+/// as subclasses of die_check (see below) and registered using
+/// reg_die_check (see further below).
+class check_die_tree
+  : public highlevel_check<check_die_tree>
+{
+public:
+  static void register_check (die_check_item *check);
+
+  static checkdescriptor const *descriptor ()
+  {
+    static checkdescriptor cd
+      (checkdescriptor::create ("check_die_tree")
+       .hidden ()
+       .description ("A pass over the DIE tree that dispatches to various per-DIE checks.\n"));
+    return &cd;
+  }
+
+  check_die_tree (checkstack &stack, dwarflint &lint);
+};
+
+class die_check
+{
+public:
+  virtual ~die_check () {}
+  virtual void die (all_dies_iterator<elfutils::dwarf> const &it) = 0;
+};
+
+template <class T>
+struct reg_die_check
+  : public die_check_item
+{
+  reg_die_check ()
+  {
+    check_die_tree::register_check (this);
+  }
+
+  virtual die_check *create (highlevel_check_i *check,
+                            checkstack &stack, dwarflint &lint)
+  {
+    return new T (check, stack, lint);
+  }
+
+  virtual checkdescriptor const *descriptor () const
+  {
+    return T::descriptor ();
+  }
+
+private:
+  /// The top-level scheduler needs to see per-DIE checks as real
+  /// checks, which they are not.  So the per-DIE registrar creates
+  /// this check stub that's here only to trick the check_die_tree to
+  /// run.  check_die_tree then does the per-DIE check scheduling
+  /// itself, down in die_check_context.
+  class check_stub
+    : public highlevel_check<check_stub>
+  {
+    check_die_tree *_m_die_tree_check;
+  public:
+    static checkdescriptor const *descriptor ()
+    {
+      return T::descriptor ();
+    }
+
+    check_stub (checkstack &stack, dwarflint &lint)
+      : highlevel_check<check_stub> (stack, lint)
+      , _m_die_tree_check (lint.check (stack, _m_die_tree_check))
+    {}
+  };
+
+  ::reg<check_stub> _m_reg_stub;
+};
+
+#endif /* _CHECK_DIE_TREE_H_ */
diff --git a/dwarflint/check_die_tree_i.hh b/dwarflint/check_die_tree_i.hh
new file mode 100644 (file)
index 0000000..95b98e6
--- /dev/null
@@ -0,0 +1 @@
+class check_die_tree;
diff --git a/dwarflint/check_duplicate_DW_tag_variable.cc b/dwarflint/check_duplicate_DW_tag_variable.cc
new file mode 100644 (file)
index 0000000..609760b
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "check_die_tree.hh"
+#include "pri.hh"
+#include "messages.hh"
+#include <map>
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_duplicate_DW_tag_variable
+    : public die_check
+  {
+    struct varinfo
+    {
+      dwarf::debug_info_entry::children_type::const_iterator decl, def;
+
+      explicit varinfo (dwarf::debug_info_entry::children_type const &children)
+       : decl (children.end ())
+       , def (children.end ())
+      {}
+
+      varinfo (varinfo const &other)
+       : decl (other.decl)
+       , def (other.def)
+      {}
+    };
+
+  public:
+    static checkdescriptor const *descriptor () {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_duplicate_DW_tag_variable")
+        .description (
+"Implements a check for two full DW_TAG_variable DIEs with the same "
+"DW_AT_name value.  This covers duplicate declaration, duplicate "
+"definition and declaration with definition.\n"
+" https://fedorahosted.org/pipermail/elfutils-devel/2010-July/001497.html\n"
+" http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39524\n"));
+      return &cd;
+    }
+
+    check_duplicate_DW_tag_variable (highlevel_check_i *,
+                                    checkstack &, dwarflint &) {}
+
+    virtual void
+    die (all_dies_iterator<dwarf> const &it)
+    {
+      dwarf::debug_info_entry::children_type const &children
+       = it->children ();
+
+      typedef std::map<std::string, varinfo> variables_map;
+      variables_map variables;
+
+      for (dwarf::debug_info_entry::children_type::const_iterator
+            jt = children.begin (); jt != children.end (); ++jt)
+       {
+         if (jt->tag () == DW_TAG_variable)
+           {
+             dwarf::debug_info_entry::attributes_type const &
+               attrs = jt->attributes ();
+             dwarf::debug_info_entry::attributes_type::const_iterator
+               at, et = attrs.end ();
+             if ((at = attrs.find (DW_AT_name)) == et)
+               continue;
+             char const *cname = (*at).second.identifier ();
+
+             bool declaration = false;
+             if ((at = attrs.find (DW_AT_declaration)) != et)
+               declaration = (*at).second.flag ();
+
+             std::string name (cname);
+             variables_map::iterator old = variables.find (name);
+             if (old == variables.end ())
+               {
+                 varinfo i (children);
+                 if (declaration)
+                   i.decl = jt;
+                 else
+                   i.def = jt;
+                 variables.insert (std::make_pair (name, i));
+               }
+             else
+               {
+                 varinfo &i = old->second;
+                 if ((declaration && i.decl != children.end ())
+                     || (!declaration && i.def != children.end ()))
+                   wr_message (die_locus (*jt), mc_impact_3 | mc_die_other)
+                     .id (descriptor ())
+                     << "Re" << (declaration ? "declaration" : "definition")
+                     << " of variable '" << name << "', originally seen at "
+                     << pri::ref (declaration ? *i.decl : *i.def)
+                     << '.' << std::endl;
+                 else
+                   wr_message (die_locus (*jt), mc_impact_3 | mc_die_other)
+                     .id (descriptor ())
+                     << "Found "
+                     << (declaration ? "declaration" : "definition")
+                     << " of variable '" << name
+                     << "' whose "
+                     << (declaration ? "definition" : "declaration")
+                     << " was seen at "
+                     << pri::ref (declaration ? *i.def : *i.decl)
+                     << '.' << std::endl;
+               }
+           }
+       }
+    }
+  };
+  reg_die_check<check_duplicate_DW_tag_variable> reg;
+}
diff --git a/dwarflint/check_dups_abstract_origin.cc b/dwarflint/check_dups_abstract_origin.cc
new file mode 100644 (file)
index 0000000..f81d957
--- /dev/null
@@ -0,0 +1,152 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "check_die_tree.hh"
+#include "pri.hh"
+#include "messages.hh"
+#include <map>
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_dups_abstract_origin
+    : public die_check
+  {
+  public:
+    static checkdescriptor const *descriptor ()
+    {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_dups_abstract_origin")
+        .description (
+"If a given attribute name is present on a DIE, it is "
+"suspicious if that attribute name appears on the DIE that's the "
+"first DIE's DW_AT_abstract_origin or DW_AT_specification.\n"
+" https://bugzilla.redhat.com/show_bug.cgi?id=527430\n"));
+      return &cd;
+    }
+
+    bool
+    duplicate_ok (int tag, int at, int from, int ref_tag, bool same)
+    {
+      // A call site entry has a DW_AT_low_pc attribute which is the return
+      // address after the call and a DW_AT_abstract_origin that is a
+      // pointer to the reference it calls directly or indirectly. So
+      // both may be available also at the abstract_origin (with different
+      // values).
+      if (tag == DW_TAG_GNU_call_site
+         && (at == DW_AT_low_pc || at == DW_AT_abstract_origin)
+         && from == DW_AT_abstract_origin
+         && ! same)
+       return true;
+
+      // A subprogram that has a concrete out-of-line instance might
+      // have an object_pointer different from the original variant
+      // of the subprogram. Similar for a subprogram specification,
+      // which may refer to the specification die of the object_pointer,
+      // while the instance of the subprogram will refer to the
+      // actual instance of the object_pointer die.
+      if (tag == DW_TAG_subprogram
+         && at == DW_AT_object_pointer
+         && (from == DW_AT_abstract_origin || from == DW_AT_specification)
+         && ref_tag == DW_TAG_subprogram
+         && ! same)
+       return true;
+
+      // A subprogram can be defined outside the body of the enclosing
+      // class, then file and/or line attributes can differ.
+      if (tag == DW_TAG_subprogram
+         && from == DW_AT_specification
+         && (at == DW_AT_decl_line || at == DW_AT_decl_file)
+         && ref_tag == DW_TAG_subprogram
+         && ! same)
+       return true;
+
+      // Same for a member variable can be defined outside the body of the
+      // enclosing class, then file and/or line attributes can differ.
+      if (tag == DW_TAG_variable
+         && from == DW_AT_specification
+         && (at == DW_AT_decl_line || at == DW_AT_decl_file)
+         && ref_tag == DW_TAG_member
+         && ! same)
+       return true;
+
+
+      return false;
+    }
+
+    void
+    check_die_attr (dwarf::debug_info_entry const &entry,
+                   dwarf::attribute const &attr)
+    {
+      std::map<unsigned int, dwarf::attr_value> m;
+      for (dwarf::debug_info_entry::attributes_type::const_iterator
+            at = entry.attributes ().begin ();
+          at != entry.attributes ().end (); ++at)
+       m.insert (std::make_pair ((*at).first, (*at).second));
+
+      dwarf::attr_value const &val = attr.second;
+      // xxx Referree can't be const&, gives memory errors.
+      dwarf::debug_info_entry referree = *val.reference ();
+
+      std::map<unsigned int, dwarf::attr_value>::const_iterator at2;
+      for (dwarf::debug_info_entry::attributes_type::const_iterator
+            at = referree.attributes ().begin ();
+          at != referree.attributes ().end (); ++at)
+       if ((at2 = m.find ((*at).first)) != m.end ()
+           && ! duplicate_ok (entry.tag (), at2->first, attr.first,
+                              referree.tag (), at2->second == (*at).second))
+         wr_message (die_locus (entry), mc_impact_3 | mc_acc_bloat | mc_die_rel)
+           .id (descriptor ())
+           << dwarf::tags::name (entry.tag ())
+           << " attribute " << dwarf::attributes::name (at2->first)
+           << " is duplicated at " << dwarf::attributes::name (attr.first)
+           << " (" << pri::ref (referree) << ")"
+           << (at2->second == (*at).second
+               ? "." : " with different value.")
+           << std::endl;
+    }
+
+    explicit
+    check_dups_abstract_origin (highlevel_check_i *, checkstack &, dwarflint &)
+    {
+      // No state necessary.
+    }
+
+    virtual void
+    die (all_dies_iterator<dwarf> const &it)
+    {
+      // Do we have DW_AT_abstract_origin or DW_AT_specification?
+      dwarf::debug_info_entry const &entry = *it;
+      for (dwarf::debug_info_entry::attributes_type::const_iterator
+            at = entry.attributes ().begin ();
+          at != entry.attributes ().end (); ++at)
+       if ((*at).first == DW_AT_abstract_origin
+           || (*at).first == DW_AT_specification)
+         {
+           assert ((*at).second.what_space () == dwarf::VS_reference);
+           check_die_attr (entry, *at);
+         }
+    }
+  };
+
+  reg_die_check<check_dups_abstract_origin> reg;
+}
diff --git a/dwarflint/check_expected_trees.cc b/dwarflint/check_expected_trees.cc
new file mode 100644 (file)
index 0000000..dd22ddf
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "../libdw/c++/dwarf-knowledge.cc"
+#include "../libdw/c++/dwarf"
+
+#include "check_debug_info.hh"
+#include "highlevel_check.hh"
+#include "expected.hh"
+#include "messages.hh"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_expected_trees
+    : public highlevel_check<check_expected_trees>
+  {
+  public:
+    static checkdescriptor const *descriptor () {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_expected_trees")
+        .description (
+"Checks whether all DIEs have the right attributes and the right children.\n"
+"Currently this is very much a work in progress.\n"));
+      return &cd;
+    }
+
+    check_expected_trees (checkstack &stack, dwarflint &lint);
+  };
+
+  reg<check_expected_trees> reg_check_expected_trees;
+
+  const expected_at_map expected_at;
+  //static const expected_children_map expected_children;
+
+  struct name_extractor {
+    int operator () (dwarf::attribute const &at) {
+      return at.first;
+    }
+  } extract_name;
+
+  std::ostream &
+  operator << (std::ostream &o, dwarf::value_space vs)
+  {
+    switch (vs)
+      {
+      case dwarf::VS_flag: return o << "flag";
+      case dwarf::VS_dwarf_constant: return o << "dwarf_constant";
+      case dwarf::VS_discr_list: return o << "discr_list";
+      case dwarf::VS_reference: return o << "reference";
+      case dwarf::VS_lineptr: return o << "lineptr";
+      case dwarf::VS_macptr: return o << "macptr";
+      case dwarf::VS_rangelistptr: return o << "rangelistptr";
+      case dwarf::VS_identifier: return o << "identifier";
+      case dwarf::VS_string: return o << "string";
+      case dwarf::VS_source_file: return o << "source_file";
+      case dwarf::VS_source_line: return o << "source_line";
+      case dwarf::VS_source_column: return o << "source_column";
+      case dwarf::VS_address: return o << "address";
+      case dwarf::VS_constant: return o << "constant";
+      case dwarf::VS_location: return o << "location";
+      };
+
+    abort ();
+  }
+}
+
+check_expected_trees::check_expected_trees (checkstack &stack, dwarflint &lint)
+  : highlevel_check<check_expected_trees> (stack, lint)
+{
+  lint.check <check_debug_info> (stack);
+
+  try
+    {
+      struct
+      {
+       void operator () (dwarf::compile_unit const &cu,
+                         dwarf::debug_info_entry const &parent)
+       {
+         die_locus where (parent);
+
+         int parent_tag = parent.tag ();
+
+         // Set of attributes of this DIE.
+         std::set <int> attributes;
+         std::transform (parent.attributes ().begin (),
+                         parent.attributes ().end (),
+                         std::inserter (attributes, attributes.end ()),
+                         extract_name);
+
+         // Attributes that we expect at this DIE.
+         expected_set::expectation_map const &expect
+           = expected_at.map (parent_tag);
+
+         // Check missing attributes.
+         for (expected_set::expectation_map::const_iterator jt
+                = expect.begin (); jt != expect.end (); ++jt)
+           {
+             std::set <int>::iterator kt = attributes.find (jt->first);
+             char const *what = NULL;
+             if (kt == attributes.end ())
+               switch (jt->second)
+                 {
+                 case opt_required:
+                   what = " lacks required attribute ";
+                   // FALL_THROUGH
+
+                 case opt_expected:
+                   if (what == NULL)
+                     what = " should contain attribute ";
+                   wr_message (where, mc_impact_2 | mc_info)
+                     << elfutils::dwarf::tags::name (parent_tag) << what
+                     << elfutils::dwarf::attributes::name (jt->first) << '.'
+                     << std::endl;
+                   break;
+
+                 case opt_optional:
+                   break;
+                 };
+           }
+
+         // Check present attributes for expected-ness, and validate
+         // value space.
+         for (dwarf::debug_info_entry::attributes_type::const_iterator
+                jt = parent.attributes ().begin (),
+                jte = parent.attributes ().end ();
+              jt != jte; ++jt)
+           {
+             unsigned name = extract_name (*jt);
+
+             expected_set::expectation_map::const_iterator
+               kt = expect.find (name);
+             if (kt == expect.end ())
+               wr_message (where, mc_impact_3 | mc_info)
+                 << ": DIE \"" << dwarf::tags::name (parent_tag)
+                 << "\" has attribute \"" << dwarf::attributes::name (name)
+                 << "\", which is not expected." << std::endl;
+
+             try
+               {
+                 unsigned exp_vs = expected_value_space (name, parent_tag);
+                 dwarf::value_space vs = (*jt).second.what_space ();
+                 if ((exp_vs & (1U << vs)) == 0)
+                   wr_message (where, mc_impact_3 | mc_info)
+                     << ": in DIE \"" << dwarf::tags::name (parent_tag)
+                     << "\", attribute \"" << dwarf::attributes::name (name)
+                     << "\" has value of unexpected type \"" << vs
+                     << "\"." << std::endl;
+               }
+             // XXX more specific class when <dwarf> has it
+             catch (...)
+               {
+                 wr_message (where, mc_impact_4 | mc_info | mc_error)
+                   << ": in DIE \"" << dwarf::tags::name (parent_tag)
+                   << "\", couldn't obtain type of attribute \""
+                   << dwarf::attributes::name (name) << "\"."
+                   << std::endl;
+               }
+           }
+
+         // Check children recursively.
+         dwarf::debug_info_entry::children_type const &children
+           = parent.children ();
+         for (dwarf::debug_info_entry::children_type::const_iterator
+                jt = children.begin (); jt != children.end (); ++jt)
+           (*this) (cu, *jt);
+       }
+      } recursively_validate;
+
+      class dwarf::compile_units_type const &cus = dw.compile_units ();
+      for (dwarf::compile_units_type::const_iterator it = cus.begin ();
+          it != cus.end (); ++it)
+       recursively_validate (*it, *it);
+    }
+  // XXX more specific class when <dwarf> has it
+  catch (std::runtime_error &exc)
+    {
+      wr_error (section_locus (sec_info))
+       << "Exception while checking expected trees: " << exc.what ()
+       << std::endl;
+      throw check_base::failed ();
+    }
+}
diff --git a/dwarflint/check_linkage_external_die.cc b/dwarflint/check_linkage_external_die.cc
new file mode 100644 (file)
index 0000000..4170f9b
--- /dev/null
@@ -0,0 +1,171 @@
+/* Check that every die that has a linkage_name is also external.
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "check_die_tree.hh"
+#include "pri.hh"
+#include "messages.hh"
+
+
+#include "../libelf/gelf.h"
+#include "../libdw/libdw.h"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_linkage_external_die
+    : public die_check
+  {
+  private:
+    std::map<std::string, bool> _m_symbols;
+
+  public:
+    static checkdescriptor const *descriptor ()
+    {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_linkage_external_die")
+        .description ("Check that each DIE that has a linkage_name "
+                      "also has an external attribute.\n"));
+      return &cd;
+    }
+
+    check_linkage_external_die (highlevel_check_i *check,
+                               checkstack &, dwarflint &)
+    {
+      // Extract all symbol table names for objects and functions
+      // and store whether they are global or not in _m_symbols.
+      Dwarf *dwarf = check->c_dw;
+      Elf *elf = dwarf_getelf (dwarf);
+      Elf_Scn *scn = NULL;
+      while ((scn = elf_nextscn (elf, scn)) != NULL)
+       {
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+         if (shdr != NULL && (shdr->sh_type == SHT_DYNSYM
+                              || shdr->sh_type == SHT_SYMTAB))
+           {
+             Elf_Data *data = elf_getdata (scn, NULL);
+             size_t shstrndx;
+             elf_getshdrstrndx (elf, &shstrndx);
+             unsigned int syms = shdr->sh_size / shdr->sh_entsize;
+             for (unsigned int cnt = 0; cnt < syms; ++cnt)
+               {
+                 GElf_Sym sym_mem;
+                 GElf_Sym *sym = gelf_getsym (data, cnt, &sym_mem);
+                 if (sym != NULL
+                     && (GELF_ST_TYPE (sym->st_info) == STT_OBJECT
+                         || GELF_ST_TYPE (sym->st_info) == STT_FUNC))
+                   {
+                     const char *name;
+                     name = elf_strptr (elf, shdr->sh_link, sym->st_name);
+                     if (name != NULL)
+                       {
+                         // Regard anything not explicitly marked as local
+                         // a global symbol, it could be STB_GLOBAL,
+                         // STB_WEAK, STB_GNU_UNIQUE, ...
+                         unsigned int binding = GELF_ST_BIND (sym->st_info);
+                         bool global = binding != STB_LOCAL;
+                         using namespace std;
+                         _m_symbols.insert (pair<string, bool>
+                                            (string (name), global));
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+    static bool is_external (all_dies_iterator<dwarf> const &it)
+    {
+      dwarf::debug_info_entry::attributes_type attrs = (*it).attributes ();
+      dwarf::debug_info_entry::attributes_type::const_iterator external
+       = attrs.find_integrate (DW_AT_external);
+
+      return external != attrs.end () && (*external).second.flag ();
+    }
+
+    virtual void
+    die (all_dies_iterator<dwarf> const &it)
+    {
+      dwarf::debug_info_entry const &entry = *it;
+      dwarf::debug_info_entry::attributes_type attrs = entry.attributes ();
+      dwarf::debug_info_entry::attributes_type::const_iterator linkage_name
+       = attrs.find (DW_AT_linkage_name);
+      if (linkage_name == attrs.end ())
+       linkage_name = attrs.find (DW_AT_MIPS_linkage_name);
+      if (linkage_name != attrs.end ())
+       {
+         using namespace std;
+         const char *name = (*linkage_name).second.string ();
+         map<string, bool>::iterator s = _m_symbols.find (string (name));
+         if (s == _m_symbols.end ())
+           {
+             // No symbol in table, OK, if not a defining or const object.
+             // GNU extension, anonymous structs, enums and unions can
+             // have a linkage_name.
+             if (attrs.find (DW_AT_declaration) == attrs.end ()
+                 && attrs.find (DW_AT_const_value) == attrs.end ()
+                 && ((entry.tag () != DW_TAG_structure_type
+                     && entry.tag () != DW_TAG_enumeration_type
+                     && entry.tag () != DW_TAG_union_type)
+                     || attrs.find (DW_AT_name) != attrs.end ()))
+               {
+                 wr_message (die_locus (entry),
+                             mc_impact_3 | mc_acc_suboptimal | mc_die_other)
+                   .id (descriptor ())
+                   << elfutils::dwarf::tags::name (entry.tag ())
+                   << " has linkage_name attribute `"
+                   << name << "', which is not in string table,"
+                   << " but DIE is not marked as a declaration"
+                   << " or const value."
+                   << std::endl;
+               }
+           }
+         else if ((*s).second == false)
+           {
+             // Local symbol in table, OK if not a defining object
+             // and marked external. Which means it comes from an
+             // external symbol table.
+             if (attrs.find (DW_AT_declaration) == attrs.end ()
+                 && is_external (it))
+               {
+                 wr_message (die_locus (entry),
+                             mc_impact_3 | mc_acc_suboptimal | mc_die_other)
+                   .id (descriptor ())
+                   << elfutils::dwarf::tags::name (entry.tag ())
+                   << " has linkage_name attribute `"
+                   << name << "', which is a local symbol."
+                   << std::endl;
+               }
+           }
+         else if (! is_external (it))
+           {
+             // Global symbol in symbol table, not marked external.
+             // Always bad.
+             wr_message (die_locus (entry),
+                         mc_impact_3 | mc_acc_suboptimal | mc_die_other)
+               .id (descriptor ())
+               << elfutils::dwarf::tags::name (entry.tag ())
+               << " has linkage_name attribute, but no external attribute."
+               << std::endl;
+           }
+       }
+    }
+  };
+
+  reg_die_check<check_linkage_external_die> reg;
+}
diff --git a/dwarflint/check_matching_ranges.cc b/dwarflint/check_matching_ranges.cc
new file mode 100644 (file)
index 0000000..972d0b6
--- /dev/null
@@ -0,0 +1,108 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "highlevel_check.hh"
+#include "check_debug_loc_range.hh"
+#include "check_debug_aranges.hh"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_matching_ranges
+    : public highlevel_check<check_matching_ranges>
+  {
+  public:
+    static checkdescriptor const *descriptor () {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_matching_ranges")
+        .description (
+"Check that the ranges in .debug_aranges and .debug_ranges match.\n"));
+      return &cd;
+    }
+
+    check_matching_ranges (checkstack &stack, dwarflint &lint);
+  };
+
+  reg<check_matching_ranges> reg_matching_ranges;
+}
+
+check_matching_ranges::check_matching_ranges (checkstack &stack,
+                                             dwarflint &lint)
+  : highlevel_check<check_matching_ranges> (stack, lint)
+{
+  lint.check<check_debug_ranges> (stack);
+  lint.check<check_debug_aranges> (stack);
+
+  try
+    {
+      char buf[128];
+      const dwarf::aranges_map &aranges = dw.aranges ();
+      for (dwarf::aranges_map::const_iterator i = aranges.begin ();
+          i != aranges.end (); ++i)
+       {
+         const dwarf::compile_unit &cu = i->first;
+         cudie_locus where_ref (cu);
+         loc_range_locus where_r (sec_ranges, where_ref);
+         arange_locus where_ar (where_ref);
+
+         std::set<dwarf::ranges::key_type>
+           cu_aranges = i->second,
+           cu_ranges = cu.ranges ();
+
+         typedef std::vector <dwarf::arange_list::value_type>
+           range_vec;
+         range_vec missing;
+         std::back_insert_iterator <range_vec> i_missing (missing);
+
+         std::set_difference (cu_aranges.begin (), cu_aranges.end (),
+                              cu_ranges.begin (), cu_ranges.end (),
+                              i_missing);
+
+         for (range_vec::iterator it = missing.begin ();
+              it != missing.end (); ++it)
+           wr_message (where_r, mc_ranges | mc_aranges | mc_impact_3)
+             << "missing range "
+             << range_fmt (buf, sizeof buf, it->first, it->second)
+             << ", present in .debug_aranges." << std::endl;
+
+         missing.clear ();
+         std::set_difference (cu_ranges.begin (), cu_ranges.end (),
+                              cu_aranges.begin (), cu_aranges.end (),
+                              i_missing);
+
+         for (range_vec::iterator it = missing.begin ();
+              it != missing.end (); ++it)
+           wr_message (where_ar, mc_ranges | mc_aranges | mc_impact_3)
+             << "missing range "
+             << range_fmt (buf, sizeof buf, it->first, it->second)
+             << ", present in .debug_ranges." << std::endl;
+       }
+    }
+  // XXX more specific class when <dwarf> has it
+  catch (std::runtime_error &exc)
+    {
+      wr_error (section_locus (sec_info))
+       << "Exception while checking matching ranges: " << exc.what ()
+       << std::endl;
+      throw check_base::failed ();
+    }
+}
diff --git a/dwarflint/check_nodebug.cc b/dwarflint/check_nodebug.cc
new file mode 100644 (file)
index 0000000..19455a5
--- /dev/null
@@ -0,0 +1,73 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "checks.hh"
+#include "messages.hh"
+#include "sections.hh"
+#include "option.hh"
+
+static void_option ignore_missing
+  ("Don't complain if files have no DWARF at all",
+   "nodebug:ignore", 'i');
+
+class check_nodebug
+  : public check<check_nodebug>
+{
+public:
+  static checkdescriptor const *descriptor ()
+  {
+    static checkdescriptor cd
+      (checkdescriptor::create ("check_nodebug")
+       .groups ("@low")
+       .option (ignore_missing)
+       .description (
+"Checks that there are at least essential debuginfo sections present "
+"in the ELF file.\n"));
+    return &cd;
+  }
+
+  check_nodebug (checkstack &stack, dwarflint &lint);
+
+private:
+  void not_available (section_id sec_id)
+  {
+    wr_error (section_locus (sec_id))
+      << "data not found." << std::endl;
+  }
+
+  template <section_id sec_id>
+  void request (checkstack &stack, dwarflint &lint)
+  {
+    if (lint.toplev_check<section<sec_id> > (stack) == NULL)
+      not_available (sec_id);
+  }
+
+};
+
+static reg<check_nodebug> reg_nodebug;
+
+check_nodebug::check_nodebug (checkstack &stack, dwarflint &lint)
+{
+  if (ignore_missing)
+    return;
+
+  // We demand .debug_info and .debug_abbrev, the rest is optional.
+  // Presence of the other sections is (or should be) requested if
+  // there are pending references from .debug_info.
+  request<sec_abbrev> (stack, lint);
+  request<sec_info> (stack, lint);
+}
diff --git a/dwarflint/check_range_out_of_scope.cc b/dwarflint/check_range_out_of_scope.cc
new file mode 100644 (file)
index 0000000..f60f18f
--- /dev/null
@@ -0,0 +1,233 @@
+/* Check whether PC ranges reported at DIE fall into the containing scope.
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "highlevel_check.hh"
+#include "coverage.hh"
+#include "pri.hh"
+#include "check_debug_loc_range.hh"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_range_out_of_scope
+    : public highlevel_check<check_range_out_of_scope>
+  {
+    typedef std::vector<std::pair< ::Dwarf_Addr, ::Dwarf_Addr> >
+      ranges_t;
+
+    static void recursively_validate (dwarf::compile_unit const &cu,
+                                     dwarf::debug_info_entry const &die,
+                                     ranges_t const &ranges,
+                                     locus const &wh_parent);
+
+  public:
+    static checkdescriptor const *descriptor () {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_range_out_of_scope")
+        .description (
+"Check whether PC ranges reported at DIEs fall into the containing scope.\n"));
+      return &cd;
+    }
+
+    check_range_out_of_scope (checkstack &stack, dwarflint &lint);
+  };
+
+  // Register the check.
+  reg<check_range_out_of_scope> reg_range_out_of_scope;
+  const ::Dwarf_Addr noaddr = -1;
+}
+
+check_range_out_of_scope::check_range_out_of_scope (checkstack &stack, dwarflint &lint)
+  : highlevel_check<check_range_out_of_scope> (stack, lint)
+{
+  try
+    {
+      class dwarf::compile_units_type const &cus = dw.compile_units ();
+      ranges_t r;
+      r.push_back (std::make_pair (0, -1));
+      section_locus wh (sec_info);
+      for (dwarf::compile_units_type::const_iterator it = cus.begin ();
+          it != cus.end (); ++it)
+       recursively_validate (*it, *it, r, wh);
+    }
+  // XXX more specific class when <dwarf> has it
+  catch (std::runtime_error &exc)
+    {
+      wr_error (section_locus (sec_info))
+       << "Exception while checking ranges out of scope: " << exc.what ()
+       << std::endl;
+      throw check_base::failed ();
+    }
+}
+
+void
+check_range_out_of_scope::recursively_validate
+  (dwarf::compile_unit const &cu,
+   dwarf::debug_info_entry const &die,
+   ranges_t const &ranges,
+   locus const &wh_parent)
+{
+  die_locus wh (die);
+
+  ::Dwarf_Addr low_pc = 0;
+  ::Dwarf_Addr high_pc = ::noaddr;
+  ranges_t my_ranges;
+  for (dwarf::debug_info_entry::attributes_type::const_iterator
+        at = die.attributes ().begin ();
+       at != die.attributes ().end (); ++at)
+    {
+      dwarf::attr_value const &value = (*at).second;
+      dwarf::value_space vs = value.what_space ();
+      if ((*at).first == DW_AT_low_pc)
+       low_pc = value.address ();
+      else if ((*at).first == DW_AT_high_pc)
+       high_pc = value.address ();
+      else if (vs == dwarf::VS_rangelistptr)
+       for (dwarf::range_list::const_iterator
+              it = value.ranges ().begin ();
+            it != value.ranges ().end (); ++it)
+         my_ranges.push_back (*it);
+    }
+
+  if (low_pc != 0 || high_pc != ::noaddr)
+    {
+      // Simultaneous appearance of both low_pc/high_pc pair
+      // and rangelist pointer is forbidden by 3.1.1 #1.
+      // Presence of low_pc on itself is OK on compile_unit
+      // and partial_unit DIEs, otherwise it serves the same
+      // purpose as low_pc/high_pc pair that covers one
+      // address point.
+
+      if (high_pc == ::noaddr
+         && die.tag () != DW_TAG_compile_unit
+         && die.tag () != DW_TAG_partial_unit)
+       high_pc = low_pc + 1;
+
+      if (high_pc != ::noaddr)
+       {
+         if (my_ranges.size () != 0)
+           wr_message (wh, mc_impact_4 | mc_info | mc_error)
+             << "both low_pc/high_pc pair and ranges present."
+             << std::endl;
+         else
+           my_ranges.push_back (std::make_pair (low_pc, high_pc));
+       }
+    }
+
+  // If my_ranges is non-empty, check that it's a subset of
+  // ranges.
+  if (my_ranges.size () != 0)
+    {
+      // xxx Extract this logic to some table.
+      switch (die.tag ())
+       {
+         /* These PC-ful DIEs should be wholly contained by
+            PC-ful parental DIE.  */
+       case DW_TAG_inlined_subroutine:
+       case DW_TAG_lexical_block:
+       case DW_TAG_entry_point:
+       case DW_TAG_label:
+       case DW_TAG_with_stmt:
+       case DW_TAG_try_block:
+       case DW_TAG_catch_block:
+         {
+           coverage cov1;
+           for (ranges_t::const_iterator it = my_ranges.begin ();
+                it != my_ranges.end (); ++it)
+             cov1.add ((*it).first, (*it).second - (*it).first);
+
+           coverage cov2;
+           for (ranges_t::const_iterator it = ranges.begin ();
+                it != ranges.end (); ++it)
+             cov2.add ((*it).first, (*it).second - (*it).first);
+
+           coverage result = cov1 - cov2;
+
+           if (!result.empty ())
+             {
+               wr_message (wh, mc_error).id (descriptor ())
+                 << "PC range " << cov::format_ranges (cov1)
+                 << " is not a sub-range of containing scope."
+                 << std::endl;
+
+               wr_message (wh_parent, mc_error).when_prev ()
+                 << "in this context: " << cov::format_ranges (cov2)
+                 << std::endl;
+             }
+         }
+       }
+    }
+
+  // xxx building the coverage for each die is a waste of time
+  ranges_t const &use_ranges
+    = my_ranges.size () > 0 ? my_ranges : ranges;
+  coverage cov;
+
+  for (ranges_t::const_iterator it = use_ranges.begin ();
+       it != use_ranges.end (); ++it)
+    cov.add ((*it).first, (*it).second - (*it).first);
+
+  // Now finally look for location attributes and check that
+  // _their_ PCs form a subset of ranges of this DIE.
+  for (dwarf::debug_info_entry::attributes_type::const_iterator
+        at = die.attributes ().begin ();
+       at != die.attributes ().end (); ++at)
+    {
+      dwarf::attr_value const &value = (*at).second;
+      dwarf::value_space vs = value.what_space ();
+
+      if (vs == dwarf::VS_location)
+       {
+         dwarf::location_attr const &loc = value.location ();
+         if (loc.is_list ())
+           {
+             bool runoff = false;
+             for (dwarf::location_attr::const_iterator
+                    lt = loc.begin (); lt != loc.end (); ++lt)
+               {
+                 ::Dwarf_Addr start = (*lt).first.first; //1st insn
+                 ::Dwarf_Addr end = (*lt).first.second; //1st past end
+                 ::Dwarf_Addr length = end - start;
+                 if (length > 0 // skip empty ranges
+                     && !cov.is_covered (start, length))
+                   wr_message (wh, mc_error)
+                     .id (descriptor (), runoff)
+                     << "attribute `"
+                     << elfutils::dwarf::attributes::name ((*at).first)
+                     << "': PC range " << pri::range (start, end)
+                     << " outside containing scope." << std::endl;
+               }
+             wr_message (wh_parent, mc_error)
+               .when (runoff)
+               << "in this context: " << cov::format_ranges (cov)
+               << '.' << std::endl;
+           }
+       }
+    }
+
+  // Check children recursively.
+  for (dwarf::debug_info_entry::children_type::const_iterator
+        jt = die.children ().begin ();
+       jt != die.children ().end (); ++jt)
+    recursively_validate (cu, *jt, use_ranges,
+                         my_ranges.size () > 0 ? wh : wh_parent);
+}
diff --git a/dwarflint/check_registrar.hh b/dwarflint/check_registrar.hh
new file mode 100644 (file)
index 0000000..b254b01
--- /dev/null
@@ -0,0 +1,57 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _CHECK_REGISTRAR_H_
+#define _CHECK_REGISTRAR_H_
+
+#include "checkdescriptor_i.hh"
+
+#include <vector>
+#include <set>
+#include <iostream>
+
+namespace check_registrar_aux
+{
+  bool be_verbose ();
+  void list_one_check (checkdescriptor const &cd);
+}
+
+template <class Item>
+class check_registrar_T
+  : protected std::vector<Item *>
+{
+  typedef std::vector<Item *> _super_t;
+public:
+
+  using _super_t::push_back;
+  using _super_t::const_iterator;
+  using _super_t::begin;
+  using _super_t::end;
+
+  typedef std::vector<checkdescriptor const *> checkdescriptors_t;
+
+  checkdescriptors_t
+  get_descriptors () const
+  {
+    checkdescriptors_t ret;
+    for (typename _super_t::const_iterator it = begin (); it != end (); ++it)
+      ret.push_back ((*it)->descriptor ());
+    return ret;
+  }
+};
+
+#endif /* _CHECK_REGISTRAR_H_ */
diff --git a/dwarflint/check_registrar_i.hh b/dwarflint/check_registrar_i.hh
new file mode 100644 (file)
index 0000000..6e84c91
--- /dev/null
@@ -0,0 +1,2 @@
+//-*-c++-*-
+struct check_registrar;
diff --git a/dwarflint/check_self_referential_die.cc b/dwarflint/check_self_referential_die.cc
new file mode 100644 (file)
index 0000000..48a6dca
--- /dev/null
@@ -0,0 +1,72 @@
+/* Check for DIEs with attributes referencing the DIE itself.
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "check_die_tree.hh"
+#include "pri.hh"
+#include "messages.hh"
+
+using elfutils::dwarf;
+
+namespace
+{
+  class check_self_referential_die
+    : public die_check
+  {
+  public:
+    static checkdescriptor const *descriptor ()
+    {
+      static checkdescriptor cd
+       (checkdescriptor::create ("check_self_referential_die")
+        .description (
+"A reference attribute referencing the DIE itself is suspicious.\n"
+"One example is a DW_AT_containing_type pointing to itself.\n"
+" https://fedorahosted.org/pipermail/elfutils-devel/2011-February/001794.html\n"
+                      ));
+      return &cd;
+    }
+
+    check_self_referential_die (highlevel_check_i *, checkstack &, dwarflint &)
+    {
+      // We don't keep any state for this die check.
+    }
+
+    virtual void
+    die (all_dies_iterator<dwarf> const &it)
+    {
+      dwarf::debug_info_entry const &entry = *it;
+      for (dwarf::debug_info_entry::attributes_type::const_iterator
+            at = entry.attributes ().begin ();
+          at != entry.attributes ().end (); ++at)
+       {
+         dwarf::attr_value const &val = (*at).second;
+         if (val.what_space () == dwarf::VS_reference)
+           {
+             dwarf::debug_info_entry ref = *val.reference ();
+             if (ref.identity () == entry.identity ())
+               wr_message (die_locus (entry),
+                           mc_impact_3 | mc_acc_suboptimal | mc_die_rel)
+                 .id (descriptor ())
+                 << dwarf::tags::name (entry.tag ())
+                 << " attribute " << dwarf::attributes::name ((*at).first)
+                 << " references DIE itself." << std::endl;
+           }
+       }
+    }
+  };
+
+  reg_die_check<check_self_referential_die> reg;
+}
diff --git a/dwarflint/checkdescriptor.cc b/dwarflint/checkdescriptor.cc
new file mode 100644 (file)
index 0000000..99ce4db
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "checkdescriptor.hh"
+#include "wrap.hh"
+#include <sstream>
+#include <cassert>
+
+std::ostream &
+operator << (std::ostream &o, checkgroups const &groups)
+{
+  o << '[';
+  for (checkgroups::const_iterator it = groups.begin ();
+       it != groups.end (); ++it)
+    {
+      if (it != groups.begin ())
+       o << ',';
+      o << *it;
+    }
+  o << ']';
+  return o;
+}
+
+checkdescriptor::create::create (char const *name)
+  : _m_name (name)
+  , _m_description (NULL)
+  , _m_hidden (false)
+  , _m_schedule (true)
+{}
+
+checkdescriptor::create::create (checkdescriptor const &base)
+  : _m_groups (base.groups ())
+  , _m_name (base.name ())
+  , _m_description (base.description ())
+  , _m_hidden (base.hidden ())
+  , _m_schedule (base.schedule ())
+  , _m_opts (base.opts ())
+{}
+
+checkdescriptor::create &
+checkdescriptor::create::groups (char const *a_groups)
+{
+  std::stringstream ss (a_groups);
+  std::string group;
+  while (ss >> group)
+    _m_groups.insert (group);
+  return *this;
+}
+
+checkdescriptor::checkdescriptor ()
+  : _m_name (NULL)
+  , _m_description (NULL)
+  , _m_groups ()
+  , _m_hidden (false)
+  , _m_schedule (true)
+  , _m_opts ()
+{}
+
+checkdescriptor::checkdescriptor (create const &c)
+  : _m_name (c._m_name)
+  , _m_description (c._m_description)
+  , _m_groups (c._m_groups)
+  , _m_hidden (c._m_hidden)
+  , _m_schedule (c._m_schedule)
+  , _m_opts (c._m_opts)
+{}
+
+bool
+checkdescriptor::in_group (std::string const &group) const
+{
+  return _m_groups.find (group) != _m_groups.end ();
+}
+
+void
+checkdescriptor::list (bool verbose) const
+{
+  const size_t columns = 70;
+
+  if (verbose)
+    std::cout << "=== " << name () << " ===";
+  else
+    std::cout << name ();
+
+  checkgroups const &g = groups ();
+  if (!g.empty ())
+    {
+      if (verbose)
+       std::cout << std::endl << "groups: ";
+      else
+       std::cout << ' ';
+      std::cout << g;
+    }
+  std::cout << std::endl;
+
+  if (verbose)
+    {
+      char const *desc = description ();
+      if (desc != NULL)
+       std::cout << wrap_str (desc, columns).join ();
+
+      options const &o = opts ();
+      if (!o.empty ())
+       {
+         std::cout << "recognized options:" << std::endl;
+         argp a = o.build_argp ();
+         argp_help (&a, stdout, ARGP_HELP_LONG, NULL);
+       }
+
+      std::cout << std::endl;
+    }
+}
diff --git a/dwarflint/checkdescriptor.hh b/dwarflint/checkdescriptor.hh
new file mode 100644 (file)
index 0000000..556eb97
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECKDESCRIPTOR_HH
+#define DWARFLINT_CHECKDESCRIPTOR_HH
+
+#include <set>
+#include <string>
+#include <iosfwd>
+#include <vector>
+
+#include "option.hh"
+
+struct checkgroups
+  : public std::set<std::string>
+{};
+std::ostream &operator << (std::ostream &o, checkgroups const &groups);
+
+struct checkdescriptor
+{
+  class create
+  {
+    friend class checkdescriptor;
+    checkgroups _m_groups;
+    char const *const _m_name;
+    char const *_m_description;
+    bool _m_hidden;
+    bool _m_schedule;
+    options _m_opts;
+
+  public:
+    create (char const *name = NULL);
+    create (checkdescriptor const &base); ///< For construction of overrides.
+    create &groups (char const *name);
+
+    create &description (char const *d)
+    {
+      _m_description = d;
+      return *this;
+    }
+
+    create hidden ()
+    {
+      _m_hidden = true;
+      return *this;
+    }
+
+    create option (option_i &opt)
+    {
+      _m_opts.add (&opt);
+      return *this;
+    }
+
+    create schedule (bool whether)
+    {
+      _m_schedule = whether;
+      return *this;
+    }
+  };
+
+  checkdescriptor ();
+  checkdescriptor (create const &c);
+
+  char const *name () const { return _m_name; }
+  char const *description () const { return _m_description; }
+
+  checkgroups const &groups () const { return _m_groups; }
+  bool in_group (std::string const &group) const;
+
+  bool hidden () const { return _m_hidden; }
+  bool schedule () const { return _m_schedule; }
+
+  options const &opts () const { return _m_opts; }
+
+  void list (bool verbose) const;
+
+private:
+  char const *const _m_name;
+  char const *const _m_description;
+  checkgroups const _m_groups;
+  bool const _m_hidden;
+  bool const _m_schedule;
+  options const _m_opts;
+};
+
+#endif//DWARFLINT_CHECKDESCRIPTOR_HH
diff --git a/dwarflint/checkdescriptor_i.hh b/dwarflint/checkdescriptor_i.hh
new file mode 100644 (file)
index 0000000..052fe1b
--- /dev/null
@@ -0,0 +1 @@
+struct checkdescriptor;
diff --git a/dwarflint/checked_read.cc b/dwarflint/checked_read.cc
new file mode 100644 (file)
index 0000000..48b1535
--- /dev/null
@@ -0,0 +1,209 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+#include <cassert>
+
+#include "checked_read.hh"
+#include "messages.hh"
+#include "misc.hh"
+
+bool
+read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
+                int *offset_sizep, locus const &loc)
+{
+  if (size32 == DWARF3_LENGTH_64_BIT)
+    {
+      if (!read_ctx_read_8ubyte (ctx, sizep))
+       {
+         wr_error (loc) << "can't read 64bit CU length.\n";
+         return false;
+       }
+
+      *offset_sizep = 8;
+    }
+  else if (size32 >= DWARF3_LENGTH_MIN_ESCAPE_CODE)
+    {
+      wr_error (loc)
+       << "unrecognized CU length escape value: " << size32 << ".\n";
+      return false;
+    }
+  else
+    {
+      *sizep = size32;
+      *offset_sizep = 4;
+    }
+
+  return true;
+}
+
+error_code
+read_address_size (struct read_ctx *ctx, bool addr_64,
+                  int *address_sizep, locus const &loc)
+{
+  uint8_t address_size;
+  if (!read_ctx_read_ubyte (ctx, &address_size))
+    {
+      wr_error (loc) << "can't read address size.\n";
+      return err_fatal;
+    }
+
+  error_code ret = err_ok;
+  if (address_size != 4 && address_size != 8)
+    {
+      /* Keep going.  Deduce the address size from ELF header, and try
+        to parse it anyway.  */
+      wr_error (loc) << "invalid address size: " << (int)address_size
+                    << " (only 4 or 8 allowed).\n";
+      address_size = addr_64 ? 8 : 4;
+      ret = err_nohl;
+    }
+  else if ((address_size == 8) != addr_64)
+    {
+      /* Keep going, we may still be able to parse it.  */
+      wr_error (loc) << "CU reports address size of " << address_size
+                    << " in " << (addr_64 ? 64 : 32) << "-bit ELF.\n";
+      ret = err_nohl;
+    }
+
+  *address_sizep = address_size;
+  return ret;
+}
+
+bool
+checked_read_uleb128 (read_ctx *ctx, uint64_t *ret,
+                     locus const &loc, const char *what)
+{
+  const unsigned char *ptr = ctx->ptr;
+  int st = read_ctx_read_uleb128 (ctx, ret);
+  if (st < 0)
+    wr_error (loc) << "can't read " << what << ".\n";
+  else if (st > 0)
+    {
+      char buf[19]; // 16 hexa digits, "0x", terminating zero
+      sprintf (buf, "%#" PRIx64, *ret);
+      wr_format_leb128_message (loc, what, buf, ptr, ctx->ptr);
+    }
+  return st >= 0;
+}
+
+bool
+checked_read_sleb128 (read_ctx *ctx, int64_t *ret,
+                     locus const &loc, const char *what)
+{
+  const unsigned char *ptr = ctx->ptr;
+  int st = read_ctx_read_sleb128 (ctx, ret);
+  if (st < 0)
+    wr_error (loc) << "can't read " << what << ".\n";
+  else if (st > 0)
+    {
+      char buf[20]; // sign, "0x", 16 hexa digits, terminating zero
+      int64_t val = *ret;
+      sprintf (buf, "%s%#" PRIx64, val < 0 ? "-" : "", val < 0 ? -val : val);
+      wr_format_leb128_message (loc, what, buf, ptr, ctx->ptr);
+    }
+  return st >= 0;
+}
+
+bool
+checked_read_leb128 (read_ctx *ctx, form_width_t width, uint64_t *ret,
+                    locus const &loc, const char *what)
+{
+  assert (width == fw_sleb || width == fw_uleb);
+  if (width == fw_sleb)
+    {
+      int64_t svalue;
+      if (!checked_read_sleb128 (ctx, &svalue, loc, what))
+       return false;
+      *ret = (uint64_t) svalue;
+      return true;
+    }
+  else
+    return checked_read_uleb128 (ctx, ret, loc, what);
+}
+
+bool
+read_sc_value (uint64_t *valuep, form_width_t width,
+              read_ctx *ctx, locus const &loc)
+{
+  switch (width)
+    {
+    case fw_0:
+      *valuep = 1;
+      return true;
+
+    case fw_1:
+    case fw_2:
+    case fw_4:
+    case fw_8:
+      return read_ctx_read_var (ctx, width, valuep);
+
+    case fw_uleb:
+    case fw_sleb:
+      return checked_read_leb128 (ctx, width, valuep,
+                                 loc, "attribute value");
+
+    case fw_unknown:
+      ;
+    }
+  UNREACHABLE;
+}
+
+bool
+read_generic_value (read_ctx *ctx,
+                   form_width_t width, storage_class_t storclass,
+                   locus const &loc, uint64_t *valuep, read_ctx *blockp)
+{
+  uint64_t value;
+  if (storclass == sc_value
+      || storclass == sc_block)
+    {
+      if (!read_sc_value (&value, width, ctx, loc))
+       return false;
+      if (valuep != NULL)
+       *valuep = value;
+      if (storclass == sc_value)
+       return true;
+    }
+
+  unsigned char const *start = ctx->ptr;
+  if (storclass == sc_string)
+    {
+      if (!read_ctx_read_str (ctx))
+       return false;
+    }
+  else if (storclass == sc_block)
+    {
+      if (!read_ctx_skip (ctx, value))
+       return false;
+    }
+
+  if (blockp != NULL)
+    {
+      if (!read_ctx_init_sub (blockp, ctx, start, ctx->ptr))
+       return false;
+    }
+
+  return true;
+}
diff --git a/dwarflint/checked_read.hh b/dwarflint/checked_read.hh
new file mode 100644 (file)
index 0000000..c2b19e8
--- /dev/null
@@ -0,0 +1,64 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECKED_READ_HH
+#define DWARFLINT_CHECKED_READ_HH
+
+#include "readctx.hh"
+#include "locus.hh"
+#include "dwarf_version.hh"
+
+enum error_code
+  {
+    err_ok,     ///< The operation passed.
+    err_fatal,  ///< The operation ended in unrecoverable error.
+    err_nohl,   ///< There was an error, but low-level checks may continue.
+  };
+
+bool read_size_extra (read_ctx *ctx, uint32_t size32, uint64_t *sizep,
+                     int *offset_sizep, locus const &loc);
+
+/// Read address size and return it via address_sizep and return 0.
+/// Address size may be 4 or 8; for other values it's set depending or
+/// addr_64, and err_nohl is returned.
+error_code read_address_size (read_ctx *ctx, bool addr_64,
+                             int *address_sizep, locus const &loc);
+
+bool checked_read_uleb128 (read_ctx *ctx, uint64_t *ret,
+                          locus const &loc, const char *what);
+
+bool checked_read_sleb128 (read_ctx *ctx, int64_t *ret,
+                          locus const &loc, const char *what);
+
+bool checked_read_leb128 (read_ctx *ctx, form_width_t width, uint64_t *ret,
+                         locus const &loc, const char *what);
+
+/// Read value depending on the form width and storage class.
+bool read_sc_value (uint64_t *valuep, form_width_t width,
+                   read_ctx *ctx, locus const &loc);
+
+/// Read value depending on the form width and storage class.
+/// Value is returned via VALUEP, if that is non-NULL; for block
+/// forms, the value is block length.  Block context is returned via
+/// BLOCKP, in non-NULL; for string class, the block is the string
+/// itself.
+bool read_generic_value (read_ctx *ctx,
+                        form_width_t width, storage_class_t storclass,
+                        locus const &loc, uint64_t *valuep,
+                        read_ctx *blockp);
+
+#endif//DWARFLINT_CHECKED_READ_HH
diff --git a/dwarflint/checkrule.cc b/dwarflint/checkrule.cc
new file mode 100644 (file)
index 0000000..7f14d72
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "checkrule.hh"
+#include "checkdescriptor.hh"
+#include "dwarflint.hh"
+#include <cassert>
+
+checkrule::checkrule (std::string const &a_name, action_t an_action)
+  : _m_name (a_name)
+  , _m_action (an_action)
+  , _m_used (false)
+{
+}
+
+checkrule_internal::checkrule_internal (std::string const &a_name,
+                                       action_t an_action)
+  : checkrule (a_name, an_action)
+{
+  mark_used ();
+}
+
+namespace
+{
+  bool
+  rule_matches (std::string const &name,
+               checkdescriptor const &cd)
+  {
+    if (name == "@all")
+      return true;
+    if (name == "@none")
+      return false;
+    if (name == cd.name ())
+      return true;
+    return cd.in_group (name);
+  }
+}
+
+bool
+checkrules::should_check (checkstack const &stack) const
+{
+  // We always allow scheduling hidden checks.  Those are service
+  // routines that the user doesn't even see it the list of checks.
+  assert (!stack.empty ());
+  if (stack.back ()->hidden ())
+    return true;
+
+  bool should = false;
+  for (const_iterator it = begin (); it != end (); ++it)
+    {
+      std::string const &rule_name = it->name ();
+      bool nflag = it->action () == checkrule::request;
+      if (nflag == should && it->used ())
+       continue;
+
+      for (checkstack::const_iterator jt = stack.begin ();
+          jt != stack.end (); ++jt)
+       if (rule_matches (rule_name, **jt))
+         {
+           it->mark_used ();
+           //std::cout << " rule: " << rule_name << " " << nflag << std::endl;
+           should = nflag;
+           break;
+         }
+    }
+
+  return should;
+}
diff --git a/dwarflint/checkrule.hh b/dwarflint/checkrule.hh
new file mode 100644 (file)
index 0000000..7c536d9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECKRULE_HH
+#define DWARFLINT_CHECKRULE_HH
+
+#include <vector>
+#include <string>
+#include "dwarflint_i.hh"
+
+struct checkrule
+{
+  enum action_t
+    {
+      forbid,
+      request,
+    };
+
+private:
+  std::string _m_name;
+  action_t _m_action;
+  mutable bool _m_used;
+
+public:
+  checkrule (std::string const &name, action_t action);
+
+  std::string const &name () const { return _m_name; }
+  action_t action () const { return _m_action; }
+  bool used () const { return _m_used; }
+  void mark_used () const { _m_used = true; }
+};
+
+// These are like normal rules, but they are initially marked as used
+// so as not to be warned about.
+struct checkrule_internal
+  : public checkrule
+{
+  checkrule_internal (std::string const &name, action_t action);
+};
+
+class checkrules
+  : public std::vector<checkrule>
+{
+public:
+  bool should_check (checkstack const &stack) const;
+};
+
+#endif//DWARFLINT_CHECKRULE_HH
diff --git a/dwarflint/checks.hh b/dwarflint/checks.hh
new file mode 100644 (file)
index 0000000..7039b63
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECKS_HH
+#define DWARFLINT_CHECKS_HH
+
+#include "locus.hh"
+#include "dwarflint.hh"
+#include "checkdescriptor.hh"
+#include "messages.hh"
+#include "check_registrar.hh"
+
+#include <string>
+#include <cassert>
+
+struct check_base
+{
+  struct failed {};
+  struct unscheduled: public failed {};
+  virtual ~check_base () {}
+};
+
+template<class T>
+class check
+  : public check_base
+{
+private:
+  template <class X>
+  friend X *dwarflint::check (checkstack &stack);
+  static void const *key ()
+  {
+    return reinterpret_cast <void const *> (&key);
+  }
+};
+
+class popper
+{
+  checkstack &_m_guard_stack;
+
+public:
+  popper (checkstack &guard_stack)
+    : _m_guard_stack (guard_stack)
+  {}
+
+  ~popper ()
+  {
+    _m_guard_stack.pop_back ();
+  }
+};
+
+template <class T>
+inline T *
+dwarflint::toplev_check (checkstack &stack, T *)
+{
+  try
+    {
+      return check<T> (stack);
+    }
+  catch (check_base::failed const &f)
+    {
+      return NULL;
+    }
+}
+
+template <class T>
+struct reg
+  : public main_check_item
+{
+  reg ()
+  {
+    dwarflint::main_registrar ()->push_back (this);
+  }
+
+  virtual void run (checkstack &stack, dwarflint &lint)
+  {
+    lint.toplev_check <T> (stack);
+  }
+
+  virtual checkdescriptor const *descriptor () const
+  {
+    return T::descriptor ();
+  }
+};
+
+#endif//DWARFLINT_CHECKS_HH
diff --git a/dwarflint/checks_i.hh b/dwarflint/checks_i.hh
new file mode 100644 (file)
index 0000000..2d40538
--- /dev/null
@@ -0,0 +1,2 @@
+struct check_base;
+class die_check;
diff --git a/dwarflint/coverage.cc b/dwarflint/coverage.cc
new file mode 100644 (file)
index 0000000..9ce6954
--- /dev/null
@@ -0,0 +1,367 @@
+/* Implementation of coverage analysis.
+
+   Copyright (C) 2008, 2009, 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "coverage.hh"
+#include "pri.hh"
+
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+
+coverage::const_iterator
+coverage::find (uint64_t start) const
+{
+  assert (!empty ());
+
+  size_t a = 0;
+  size_t b = size ();
+
+  while (a < b)
+    {
+      size_t i = (a + b) / 2;
+      cov_range const &r = at (i);
+
+      if (r.start > start)
+       b = i;
+      else if (r.start < start)
+       a = i + 1;
+      else
+       return begin () + i;
+    }
+
+  return begin () + a;
+}
+
+coverage::iterator
+coverage::find (uint64_t start)
+{
+  const_iterator it = const_cast<coverage const *> (this)->find (start);
+  return begin () + (it - begin ());
+}
+
+void
+coverage::add (uint64_t start, uint64_t length)
+{
+  cov_range nr = (struct cov_range){start, length};
+  if (empty ())
+    {
+      push_back (nr);
+      return;
+    }
+
+  iterator r_i = find (start);
+
+  cov_range *to_insert = &nr;
+  cov_range *coalesce = &nr;
+
+  // Coalesce with previous range?
+  if (r_i > begin ())
+    {
+      iterator p_i = r_i - 1;
+      if (coalesce->start <= p_i->start + p_i->length)
+       {
+         uint64_t coalesce_end = coalesce->start + coalesce->length;
+         if (coalesce_end > p_i->start + p_i->length)
+           {
+             p_i->length = coalesce_end - p_i->start;
+             coalesce = &*p_i;
+           }
+         else
+           coalesce = NULL;
+         to_insert = NULL;
+       }
+    }
+
+  // Coalesce with one or more following ranges?
+  if (coalesce != NULL && r_i != end ())
+    {
+      iterator p_i = r_i;
+      while (p_i != end ()
+            && coalesce->start + coalesce->length >= p_i->start)
+       {
+         uint64_t p_end = p_i->start + p_i->length;
+         if (p_end > coalesce->start + coalesce->length)
+           coalesce->length = p_end - coalesce->start;
+         if (to_insert != NULL)
+           {
+             *p_i = *to_insert;
+             to_insert = NULL;
+             coalesce = &*p_i;
+             assert (p_i == r_i);
+             ++r_i; // keep this element
+           }
+         ++p_i;
+       }
+      if (p_i > r_i)
+       erase (r_i, p_i);
+    }
+
+  if (to_insert != NULL)
+    {
+      size_t idx = r_i - begin ();
+      insert (begin () + idx, *to_insert);
+    }
+}
+
+bool
+coverage::remove (uint64_t start,
+                 uint64_t length)
+{
+  uint64_t a_end = start + length;
+  if (empty () || start == a_end)
+    return false;
+
+  iterator r_i = find (start);
+  iterator erase_begin_i = end ();
+  iterator erase_end_i = r_i; // end exclusive
+  bool overlap = false;
+
+  // Cut from previous range?
+  if (r_i > begin ())
+    {
+      iterator p_i = r_i - 1;
+      if (start < p_i->start + p_i->length)
+       {
+         uint64_t r_end = p_i->start + p_i->length;
+         // Do we cut the beginning of the range?
+         if (start == p_i->start)
+           p_i->length = a_end >= r_end ? 0 : r_end - a_end;
+         else
+           {
+             p_i->length = start - p_i->start;
+             // Do we shoot a hole in that range?
+             if (a_end < r_end)
+               {
+                 add (a_end, r_end - a_end);
+                 return true;
+               }
+           }
+
+         overlap = true;
+         if (p_i->length == 0)
+           erase_begin_i = p_i;
+       }
+    }
+
+  if (erase_begin_i == end ())
+    erase_begin_i = r_i;
+
+  // Cut from next range?
+  while (r_i < end () && r_i->start < a_end)
+    {
+      overlap = true;
+      if (a_end >= r_i->start + r_i->length)
+       {
+         ++erase_end_i;
+         ++r_i;
+       }
+      else
+       {
+         uint64_t end0 = r_i->start + r_i->length;
+         r_i->length = end0 - a_end;
+         r_i->start = a_end;
+         assert (end0 == r_i->start + r_i->length);
+       }
+    }
+
+  // Did we cut out anything completely?
+  if (erase_end_i > erase_begin_i)
+    erase (erase_begin_i, erase_end_i);
+
+  return overlap;
+}
+
+bool
+coverage::is_covered (uint64_t start, uint64_t length) const
+{
+  if (empty ())
+    return false;
+
+  const_iterator r_i = find (start);
+  uint64_t a_end = start + length;
+  if (r_i < end ())
+    if (start >= r_i->start)
+      return a_end <= r_i->start + r_i->length;
+
+  if (r_i > begin ())
+    {
+      --r_i;
+      return a_end <= r_i->start + r_i->length;
+    }
+
+  return false;
+}
+
+char *
+range_fmt (char *buf, size_t buf_size, uint64_t start, uint64_t end)
+{
+  std::stringstream ss;
+  ss << pri::range (start, end);
+  std::string s = ss.str ();
+  strncpy (buf, s.c_str (), buf_size);
+  return buf;
+}
+
+namespace
+{
+  bool overlaps (uint64_t start, uint64_t end, cov_range const &r)
+  {
+    return (start >= r.start && start < r.start + r.length)
+      || (end > r.start && end <= r.start + r.length)
+      || (start < r.start && end > r.start + r.length);
+  }
+}
+
+bool
+coverage::is_overlap (uint64_t start, uint64_t length) const
+{
+  if (empty ())
+    return false;
+  if (length == 0)
+    return is_covered (start, length);
+
+  uint64_t a_end = start + length;
+  const_iterator r_i = find (start);
+
+  if (r_i < end () && overlaps (start, a_end, *r_i))
+    return true;
+
+  if (r_i > begin ())
+    return overlaps (start, a_end, *--r_i);
+
+  return false;
+}
+
+bool
+coverage::find_holes (uint64_t start, uint64_t length,
+                     bool (*hole)(uint64_t start, uint64_t length,
+                                  void *user_data),
+                     void *user_data) const
+{
+  if (length == 0)
+    return true;
+
+  if (empty ())
+    return hole (start, length, user_data);
+
+  if (start < front ().start)
+    if (!hole (start, front ().start - start, user_data))
+      return false;
+
+  for (size_t i = 0; i < size () - 1; ++i)
+    {
+      uint64_t end_i = at (i).end ();
+      if (!hole (end_i, at (i+1).start - end_i, user_data))
+       return false;
+    }
+
+  if (start + length > back ().end ())
+    {
+      uint64_t end_last = back ().end ();
+      return hole (end_last, start + length - end_last, user_data);
+    }
+
+  return true;
+}
+
+bool
+coverage::find_ranges (bool (*cb)(uint64_t start, uint64_t length, void *data),
+                      void *user_data) const
+{
+  for (const_iterator it = begin (); it != end (); ++it)
+    if (!cb (it->start, it->length, user_data))
+      return false;
+
+  return true;
+}
+
+void
+coverage::add_all (coverage const &other)
+{
+  for (size_t i = 0; i < other.size (); ++i)
+    add (other[i].start, other[i].length);
+}
+
+bool
+coverage::remove_all (coverage const &other)
+{
+  bool ret = false;
+  for (size_t i = 0; i < other.size (); ++i)
+    if (remove (other[i].start, other[i].length))
+      ret = true;
+  return ret;
+}
+
+coverage
+coverage::operator+ (coverage const &rhs) const
+{
+  coverage ret = *this;
+  ret.add_all (rhs);
+  return ret;
+}
+
+coverage
+coverage::operator- (coverage const &rhs) const
+{
+  coverage ret = *this;
+  ret.remove_all (rhs);
+  return ret;
+}
+
+
+bool
+cov::_format_base::fmt (uint64_t start, uint64_t length)
+{
+  if (_m_seen)
+    _m_os << _m_delim;
+  _m_os << pri::range (start, start + length);
+  _m_seen = true;
+  return true;
+}
+
+bool
+cov::_format_base::wrap_fmt (uint64_t start, uint64_t length, void *data)
+{
+  _format_base *self = static_cast <_format_base *> (data);
+  return self->fmt (start, length);
+}
+
+cov::_format_base::_format_base (std::string const &delim)
+  : _m_delim (delim),
+    _m_seen (false)
+{}
+
+cov::format_ranges::format_ranges (coverage const &cov,
+                                  std::string const &delim)
+  : _format_base (delim)
+{
+  cov.find_ranges (&wrap_fmt, this);
+}
+
+cov::format_holes::format_holes (coverage const &cov,
+                                uint64_t start, uint64_t length,
+                                std::string const &delim)
+  : _format_base (delim)
+{
+  cov.find_holes (start, length, &wrap_fmt, this);
+}
diff --git a/dwarflint/coverage.hh b/dwarflint/coverage.hh
new file mode 100644 (file)
index 0000000..7d8fe28
--- /dev/null
@@ -0,0 +1,142 @@
+/* Coverage analysis, C++ support.
+
+   Copyright (C) 2008, 2009, 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_COVERAGE_HH
+#define DWARFLINT_COVERAGE_HH
+
+#include <string>
+#include <sstream>
+#include <vector>
+#include <cstdint>
+
+/* Functions and data structures for handling of address range
+   coverage.  We use that to find holes of unused bytes in DWARF
+   string table.  */
+
+struct cov_range
+{
+  uint64_t start;
+  uint64_t length;
+
+  uint64_t end () const { return start + length; }
+
+  bool operator== (cov_range const &rhs) const
+  {
+    return start == rhs.start
+      && length == rhs.length;
+  }
+};
+
+struct coverage
+  : private std::vector<cov_range>
+{
+  iterator find (uint64_t start);
+  const_iterator find (uint64_t start) const;
+
+public:
+  using std::vector<cov_range>::front;
+  using std::vector<cov_range>::back;
+  using std::vector<cov_range>::size;
+  using std::vector<cov_range>::empty;
+
+  void add (uint64_t start, uint64_t length);
+
+  /// Returns true if something was actually removed, false if whole
+  /// range falls into hole in coverage.
+  bool remove (uint64_t start, uint64_t length);
+
+  void add_all (coverage const &other);
+
+  // Returns true if something was actually removed, false if whole
+  // range falls into hole in coverage.
+  bool remove_all (coverage const &other);
+
+  bool find_ranges (bool (*cb)(uint64_t start, uint64_t length, void *data),
+                   void *data) const;
+
+  /// Returns true if whole range ADDRESS/LENGTH is covered by COV.
+  /// If LENGTH is zero, it's checked that the address is inside or at
+  /// the edge of covered range, or that there is a zero-length range
+  /// at that address.
+  bool is_covered (uint64_t start, uint64_t length) const;
+
+  /// Returns true if at least some of the range ADDRESS/LENGTH is
+  /// covered by COV.  Zero-LENGTH range never overlaps.  */
+  bool is_overlap (uint64_t start, uint64_t length) const;
+
+  bool find_holes (uint64_t start, uint64_t length,
+                  bool (*cb)(uint64_t start, uint64_t length, void *data),
+                  void *data) const;
+
+  coverage operator+ (coverage const &rhs) const;
+  coverage operator- (coverage const &rhs) const;
+  bool operator== (coverage const &rhs) const
+  {
+    return static_cast<std::vector<cov_range> > (rhs) == *this;
+  }
+};
+
+char *range_fmt (char *buf, size_t buf_size,
+                uint64_t start, uint64_t end);
+
+namespace cov
+{
+  class _format_base
+  {
+  protected:
+    std::string const &_m_delim;
+    std::ostringstream _m_os;
+    bool _m_seen;
+
+    inline bool fmt (uint64_t start, uint64_t length);
+    static bool wrap_fmt (uint64_t start, uint64_t length, void *data);
+    _format_base (std::string const &delim);
+
+  public:
+    inline operator std::string () const
+    {
+      return _m_os.str ();
+    }
+  };
+
+  struct format_ranges
+    : public _format_base
+  {
+    format_ranges (coverage const &cov, std::string const &delim = ", ");
+  };
+
+  struct format_holes
+    : public _format_base
+  {
+    format_holes (coverage const &cov, uint64_t start, uint64_t length,
+                 std::string const &delim = ", ");
+  };
+}
+
+inline std::ostream &
+operator << (std::ostream &os, cov::format_ranges const &obj)
+{
+  return os << std::string (obj);
+}
+
+inline std::ostream &
+operator << (std::ostream &os, cov::format_holes const &obj)
+{
+  return os << std::string (obj);
+}
+#endif//DWARFLINT_COVERAGE_HH
diff --git a/dwarflint/cu_coverage.cc b/dwarflint/cu_coverage.cc
new file mode 100644 (file)
index 0000000..fd94178
--- /dev/null
@@ -0,0 +1,42 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "cu_coverage.hh"
+#include "check_debug_info.hh"
+#include "check_debug_loc_range.hh"
+#include <cstring>
+
+checkdescriptor const *
+cu_coverage::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("cu_coverage")
+     .hidden ());
+  return &cd;
+}
+
+cu_coverage::cu_coverage (checkstack &stack, dwarflint &lint)
+  : _m_info (lint.check (stack, _m_info))
+  , _m_ranges (lint.check_if (_m_info->need_ranges (), stack, _m_ranges))
+  , cov (_m_info->cov ()
+        + (_m_ranges != NULL ? _m_ranges->cov () : coverage ()))
+{
+}
diff --git a/dwarflint/cu_coverage.hh b/dwarflint/cu_coverage.hh
new file mode 100644 (file)
index 0000000..c7a5b97
--- /dev/null
@@ -0,0 +1,41 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CU_COVERAGE_HH
+#define DWARFLINT_CU_COVERAGE_HH
+
+#include "check_debug_info_i.hh"
+#include "check_debug_loc_range_i.hh"
+#include "coverage.hh"
+#include "checks.hh"
+
+/** The pass for finalizing cu_coverage.  */
+class cu_coverage
+  : public check<cu_coverage>
+{
+  check_debug_info *_m_info;
+  check_debug_ranges *_m_ranges;
+
+public:
+  static checkdescriptor const *descriptor ();
+
+  coverage cov;
+
+  cu_coverage (checkstack &stack, dwarflint &lint);
+};
+
+#endif//DWARFLINT_CU_COVERAGE_HH
diff --git a/dwarflint/cu_coverage_i.hh b/dwarflint/cu_coverage_i.hh
new file mode 100644 (file)
index 0000000..01d567f
--- /dev/null
@@ -0,0 +1 @@
+class cu_coverage;
diff --git a/dwarflint/die_locus.cc b/dwarflint/die_locus.cc
new file mode 100644 (file)
index 0000000..a6a1667
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "die_locus.hh"
+#include "pri.hh"
+
+char const *
+locus_simple_fmt::cu_n ()
+{
+  return "CU";
+}
+
+std::string
+die_locus::format (bool brief) const
+{
+  std::stringstream ss;
+  if (!brief)
+    ss << section_name[sec_info] << ": ";
+
+  ss << "DIE 0x" << std::hex << _m_offset;
+
+  if (_m_attrib_name != -1)
+    ss << ", attr. " << pri::attr_name (_m_attrib_name);
+
+  return ss.str ();
+}
+
diff --git a/dwarflint/die_locus.hh b/dwarflint/die_locus.hh
new file mode 100644 (file)
index 0000000..bff85be
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DWARFLINT_DIE_LOCUS_H_
+#define _DWARFLINT_DIE_LOCUS_H_
+
+#include "locus.hh"
+#include "../libdw/c++/dwarf"
+
+namespace locus_simple_fmt
+{
+  char const *cu_n ();
+};
+
+typedef fixed_locus<sec_info,
+                   locus_simple_fmt::cu_n,
+                   locus_simple_fmt::dec> cu_locus;
+
+class die_locus
+  : public locus
+{
+  Dwarf_Off _m_offset;
+  int _m_attrib_name;
+
+public:
+  explicit die_locus (Dwarf_Off offset = -1, int attrib_name = -1)
+    : _m_offset (offset)
+    , _m_attrib_name (attrib_name)
+  {}
+
+  template <class T>
+  explicit die_locus (T const &die, int attrib_name = -1)
+    : _m_offset (die.offset ())
+    , _m_attrib_name (attrib_name)
+  {}
+
+  void
+  set_attrib_name (int attrib_name)
+  {
+    _m_attrib_name = attrib_name;
+  }
+
+  std::string format (bool brief = false) const;
+};
+
+#endif /* _DWARFLINT_DIE_LOCUS_H_ */
diff --git a/dwarflint/dwarf_2.cc b/dwarflint/dwarf_2.cc
new file mode 100644 (file)
index 0000000..c73898e
--- /dev/null
@@ -0,0 +1,169 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dwarf_version-imp.hh"
+#include "dwarf_2.hh"
+#include "../libdw/dwarf.h"
+
+namespace
+{
+  typedef preset_attribute<cl_reference, cl_constant> const_or_ref_attribute;
+
+  struct dwarf_2_attributes
+    : public attribute_table
+  {
+    dwarf_2_attributes ()
+    {
+      // Note about location descriptions in DWARF 2 (and 3).  In
+      // DWARF 2, location expressions can have classes of cl_constant
+      // or cl_block.  But we need to tell those block expressions
+      // from any old block attribute to validate the expression, and
+      // those constants from any old number to validate the
+      // reference.  So we retrofit all the DW_FORM_block* forms and
+      // appropriate attributes with cl_exprloc form DWARF 4 and
+      // cl_loclistptr (even though in DWARF 4 it's actually only
+      // DW_FORM_exprloc that has this class).
+      //
+      // Similarly with cl_lineptr and cl_macptr.  cl_rangelistptr
+      // wasn't introduced until DWARF 3.
+
+      add (ref_attribute (DW_AT_sibling));
+      add (location_attribute (DW_AT_location));
+      add (string_attribute (DW_AT_name));
+      add (const_attribute (DW_AT_ordering));
+      add (const_attribute (DW_AT_byte_size));
+      add (const_attribute (DW_AT_bit_offset));
+      add (const_attribute (DW_AT_bit_size));
+      add (attribute (DW_AT_stmt_list, cl_lineptr));
+      add (addr_attribute (DW_AT_low_pc));
+      add (addr_attribute (DW_AT_high_pc));
+      add (const_attribute (DW_AT_language));
+      add (ref_attribute (DW_AT_discr));
+      add (const_attribute (DW_AT_discr_value));
+      add (const_attribute (DW_AT_visibility));
+      add (ref_attribute (DW_AT_import));
+      add (location_attribute (DW_AT_string_length));
+      add (ref_attribute (DW_AT_common_reference));
+      add (string_attribute (DW_AT_comp_dir));
+      add (attribute (DW_AT_const_value,
+                     dw_class_set (cl_string, cl_constant, cl_block)));
+      add (ref_attribute (DW_AT_containing_type));
+      add (ref_attribute (DW_AT_default_value));
+      add (const_attribute (DW_AT_inline));
+      add (flag_attribute (DW_AT_is_optional));
+      add (const_or_ref_attribute (DW_AT_lower_bound));
+      add (string_attribute (DW_AT_producer));
+      add (flag_attribute (DW_AT_prototyped));
+      add (location_attribute (DW_AT_return_addr));
+      add (const_attribute (DW_AT_start_scope));
+      add (const_attribute (DW_AT_bit_stride));
+      add (const_or_ref_attribute (DW_AT_upper_bound));
+      add (ref_attribute (DW_AT_abstract_origin));
+      add (const_attribute (DW_AT_accessibility));
+      add (const_attribute (DW_AT_address_class));
+      add (flag_attribute (DW_AT_artificial));
+      add (ref_attribute (DW_AT_base_types));
+      add (const_attribute (DW_AT_calling_convention));
+      add (const_or_ref_attribute (DW_AT_count));
+      add (static_location_attribute (DW_AT_data_member_location));
+      add (const_attribute (DW_AT_decl_column));
+      add (const_attribute (DW_AT_decl_file));
+      add (const_attribute (DW_AT_decl_line));
+      add (flag_attribute (DW_AT_declaration));
+      add (block_attribute (DW_AT_discr_list));
+      add (const_attribute (DW_AT_encoding));
+      add (flag_attribute (DW_AT_external));
+      add (location_attribute (DW_AT_frame_base));
+      add (ref_attribute (DW_AT_friend));
+      add (const_attribute (DW_AT_identifier_case));
+      add (attribute (DW_AT_macro_info, cl_macptr));
+      add (block_attribute (DW_AT_namelist_item));
+      add (ref_attribute (DW_AT_priority));
+      add (location_attribute (DW_AT_segment));
+      add (ref_attribute (DW_AT_specification));
+      add (location_attribute (DW_AT_static_link));
+      add (ref_attribute (DW_AT_type));
+      add (location_attribute (DW_AT_use_location));
+      add (flag_attribute (DW_AT_variable_parameter));
+      add (const_attribute (DW_AT_virtuality));
+      add (static_location_attribute (DW_AT_vtable_elem_location));
+    }
+  };
+
+  struct exprloc_form
+    : public preset_form<sc_block, cl_exprloc, cl_block>
+  {
+    exprloc_form (int a_name, form_width_t a_width)
+      : preset_form<sc_block, cl_exprloc, cl_block> (a_name, a_width)
+    {}
+  };
+
+  struct dwarf_2_forms
+    : public form_table
+  {
+    dwarf_2_forms ()
+    {
+      add (exprloc_form (DW_FORM_block, fw_uleb));
+      add (exprloc_form (DW_FORM_block1, fw_1));
+      add (exprloc_form (DW_FORM_block2, fw_2));
+      add (exprloc_form (DW_FORM_block4, fw_4));
+
+      // These constant forms can in theory, in legal DWARF 2,
+      // represent various pointers.
+      typedef preset_form<sc_value,
+                         cl_constant, cl_lineptr, cl_loclistptr,
+                         cl_macptr> dw2_data_form;
+
+      add (dw2_data_form (DW_FORM_data1, fw_1));
+      add (dw2_data_form (DW_FORM_data2, fw_2));
+      add (dw2_data_form (DW_FORM_data4, fw_4));
+      add (dw2_data_form (DW_FORM_data8, fw_8));
+      add (dw2_data_form (DW_FORM_sdata, fw_sleb));
+      add (dw2_data_form (DW_FORM_udata, fw_uleb));
+
+      add (flag_form (DW_FORM_flag, fw_1));
+
+      add (ref_form (DW_FORM_ref1, fw_1));
+      add (ref_form (DW_FORM_ref2, fw_2));
+      add (ref_form (DW_FORM_ref4, fw_4));
+      add (ref_form (DW_FORM_ref8, fw_8, fb_64));
+      add (ref_form (DW_FORM_ref_udata, fw_uleb));
+
+      add (string_form (DW_FORM_string));
+      add (offset_form (DW_FORM_strp, cl_string));
+      add (address_form (DW_FORM_addr, cl_address));
+      add (address_form (DW_FORM_ref_addr, cl_reference));
+
+      add (form (DW_FORM_indirect, cl_indirect, fw_uleb, sc_value));
+    }
+  };
+
+  struct dwarf_2_t
+    : public std_dwarf
+  {
+    dwarf_2_t ()
+      : std_dwarf (dwarf_2_attributes (), dwarf_2_forms ())
+    {}
+  };
+}
+
+dwarf_version const *
+dwarf_2 ()
+{
+  static dwarf_2_t dw;
+  return &dw;
+}
diff --git a/dwarflint/dwarf_2.hh b/dwarflint/dwarf_2.hh
new file mode 100644 (file)
index 0000000..84f11f2
--- /dev/null
@@ -0,0 +1,26 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_2_HH
+#define DWARFLINT_DWARF_2_HH
+
+#include "dwarf_version_i.hh"
+
+dwarf_version const *dwarf_2 ();
+
+#endif//DWARFLINT_DWARF_2_HH
diff --git a/dwarflint/dwarf_3.cc b/dwarflint/dwarf_3.cc
new file mode 100644 (file)
index 0000000..913029c
--- /dev/null
@@ -0,0 +1,141 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dwarf_version-imp.hh"
+#include "dwarf_2.hh"
+#include "dwarf_3.hh"
+#include "../libdw/dwarf.h"
+#include <cassert>
+
+namespace
+{
+  struct dwarf_3_attributes
+    : public attribute_table
+  {
+    dwarf_3_attributes ()
+    {
+      add (location_attribute (DW_AT_location));
+      add (dynval_attribute (DW_AT_byte_size));
+      add (dynval_attribute (DW_AT_bit_offset));
+      add (dynval_attribute (DW_AT_bit_size));
+      add (location_attribute (DW_AT_string_length));
+      add (dynval_attribute (DW_AT_lower_bound));
+      add (location_attribute (DW_AT_return_addr));
+
+      // Note, DWARF 3 claims only a const class for DW_AT_bit_stride,
+      // but from 2.19 it's clear that this is an omission.
+      add (dynval_attribute (DW_AT_bit_stride));
+
+      add (dynval_attribute (DW_AT_upper_bound));
+      add (dynval_attribute (DW_AT_count));
+      add (attribute (DW_AT_data_member_location,
+                     dw_class_set (cl_exprloc, cl_constant, cl_loclistptr)));
+      add (location_attribute (DW_AT_frame_base));
+      add (location_attribute (DW_AT_segment));
+      add (location_attribute (DW_AT_static_link));
+      add (location_attribute (DW_AT_use_location));
+      add (location_attribute (DW_AT_vtable_elem_location));
+      add (dynval_attribute (DW_AT_allocated));
+      add (dynval_attribute (DW_AT_associated));
+      add (attribute (DW_AT_data_location, cl_exprloc));
+      add (dynval_attribute (DW_AT_byte_stride));
+      add (addr_attribute (DW_AT_entry_pc));
+      add (flag_attribute (DW_AT_use_UTF8));
+      add (ref_attribute (DW_AT_extension));
+      add (attribute (DW_AT_ranges, cl_rangelistptr));
+      add (attribute (DW_AT_trampoline,
+                     dw_class_set (cl_address, cl_flag,
+                                   cl_reference, cl_string)));
+      add (const_attribute (DW_AT_call_column));
+      add (const_attribute (DW_AT_call_file));
+      add (const_attribute (DW_AT_call_line));
+      add (string_attribute (DW_AT_description));
+      add (const_attribute (DW_AT_binary_scale));
+      add (const_attribute (DW_AT_decimal_scale));
+      add (ref_attribute (DW_AT_small));
+      add (const_attribute (DW_AT_decimal_sign));
+      add (const_attribute (DW_AT_digit_count));
+      add (string_attribute (DW_AT_picture_string));
+      add (flag_attribute (DW_AT_mutable));
+      add (flag_attribute (DW_AT_threads_scaled));
+      add (flag_attribute (DW_AT_explicit));
+      add (ref_attribute (DW_AT_object_pointer));
+      add (const_attribute (DW_AT_endianity));
+      add (flag_attribute (DW_AT_elemental));
+      add (flag_attribute (DW_AT_pure));
+      add (flag_attribute (DW_AT_recursive));
+    }
+  };
+
+  struct dwarf_3_forms
+    : public form_table
+  {
+    dwarf_3_forms ()
+    {
+      add (offset_form (DW_FORM_ref_addr, cl_reference));
+
+      // In DWARF 2 we made all the const forms into various cl_*ptr,
+      // since that's how the standard was worded: it allowed
+      // DW_AT_location to have any constant form.  In DWARF 3, only
+      // data4 and data8 are like this.  In addition, these two can
+      // also be cl_rangelistptr.
+      typedef preset_form<sc_value,
+                         cl_constant, cl_lineptr, cl_loclistptr,
+                         cl_macptr, cl_rangelistptr> dw3_data_form;
+
+      add (const_form (DW_FORM_data1, fw_1));
+      add (const_form (DW_FORM_data2, fw_2));
+      add (dw3_data_form (DW_FORM_data4, fw_4));
+      add (dw3_data_form (DW_FORM_data8, fw_8));
+      add (const_form (DW_FORM_sdata, fw_sleb));
+      add (const_form (DW_FORM_udata, fw_uleb));
+    }
+  };
+
+  struct dwarf_3_ext_t
+    : public std_dwarf
+  {
+    dwarf_3_ext_t ()
+      : std_dwarf (dwarf_3_attributes (), dwarf_3_forms ())
+    {}
+
+    dw_class
+    ambiguous_class (__attribute__ ((unused)) form const *form,
+                    attribute const *attribute,
+                    dw_class_set const &candidates) const
+    {
+      assert (attribute->name () == DW_AT_data_member_location);
+      assert (candidates == dw_class_set (cl_constant, cl_loclistptr));
+      return cl_loclistptr;
+    }
+  };
+}
+
+dwarf_version const *
+dwarf_3_ext ()
+{
+  static dwarf_3_ext_t dw;
+  return &dw;
+}
+
+dwarf_version const *
+dwarf_3 ()
+{
+  static dwarf_version const *dw =
+    dwarf_version::extend (dwarf_2 (), dwarf_3_ext ());
+  return dw;
+}
diff --git a/dwarflint/dwarf_3.hh b/dwarflint/dwarf_3.hh
new file mode 100644 (file)
index 0000000..c3e017a
--- /dev/null
@@ -0,0 +1,30 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_3_HH
+#define DWARFLINT_DWARF_3_HH
+
+#include "dwarf_version_i.hh"
+
+/// Pure DWARF 3 extension.
+dwarf_version const *dwarf_3_ext ();
+
+/// DWARF 3 and below.
+dwarf_version const *dwarf_3 ();
+
+#endif//DWARFLINT_DWARF_3_HH
diff --git a/dwarflint/dwarf_4.cc b/dwarflint/dwarf_4.cc
new file mode 100644 (file)
index 0000000..e398f76
--- /dev/null
@@ -0,0 +1,97 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dwarf_version-imp.hh"
+#include "dwarf_3.hh"
+#include "dwarf_4.hh"
+#include "../libdw/dwarf.h"
+
+namespace
+{
+  struct dwarf_4_attributes
+    : public attribute_table
+  {
+    dwarf_4_attributes ()
+    {
+      add (attribute (DW_AT_high_pc, dw_class_set (cl_address, cl_constant)));
+      add (ref_attribute (DW_AT_namelist_item));
+      add (ref_attribute (DW_AT_signature));
+      add (flag_attribute (DW_AT_main_subprogram));
+      add (const_attribute (DW_AT_data_bit_offset));
+      add (flag_attribute (DW_AT_const_expr));
+      add (flag_attribute (DW_AT_enum_class));
+      add (string_attribute (DW_AT_linkage_name));
+    }
+  };
+
+  struct exprloc_form
+    : public preset_form<sc_block, cl_exprloc>
+  {
+    exprloc_form (int a_name)
+      : preset_form<sc_block, cl_exprloc> (a_name, fw_uleb)
+    {}
+  };
+
+  struct dwarf_4_forms
+    : public form_table
+  {
+    dwarf_4_forms ()
+    {
+      add (const_form (DW_FORM_data4, fw_4));
+      add (const_form (DW_FORM_data8, fw_8));
+      add (offset_form (DW_FORM_sec_offset,
+                       dw_class_set (cl_lineptr, cl_loclistptr,
+                                     cl_macptr, cl_rangelistptr)));
+      add (exprloc_form (DW_FORM_exprloc));
+      add (flag_form (DW_FORM_flag_present, fw_0));
+
+      // http://wiki.dwarfstd.org/index.php?title=COMDAT_Type_Sections
+      add (ref_form (DW_FORM_ref_sig8, fw_8));
+
+      // In DWARF 2 we claim that blocks are exprloc forms (see
+      // comment there).  Revert back to pure blocks now that we have
+      // proper support for cl_exprloc.
+      add (block_form (DW_FORM_block, fw_uleb));
+      add (block_form (DW_FORM_block1, fw_1));
+      add (block_form (DW_FORM_block2, fw_2));
+      add (block_form (DW_FORM_block4, fw_4));
+    }
+  };
+
+  struct dwarf_4_ext_t
+    : public std_dwarf
+  {
+    dwarf_4_ext_t ()
+      : std_dwarf (dwarf_4_attributes (), dwarf_4_forms ())
+    {}
+  };
+}
+
+dwarf_version const *
+dwarf_4_ext ()
+{
+  static dwarf_4_ext_t dw;
+  return &dw;
+}
+
+dwarf_version const *
+dwarf_4 ()
+{
+  static dwarf_version const *dw =
+    dwarf_version::extend (dwarf_3 (), dwarf_4_ext ());
+  return dw;
+}
diff --git a/dwarflint/dwarf_4.hh b/dwarflint/dwarf_4.hh
new file mode 100644 (file)
index 0000000..50d32c9
--- /dev/null
@@ -0,0 +1,30 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_4_HH
+#define DWARFLINT_DWARF_4_HH
+
+#include "dwarf_version_i.hh"
+
+/// Pure DWARF 4 extension.
+dwarf_version const *dwarf_4_ext ();
+
+/// DWARF 4 and below.
+dwarf_version const *dwarf_4 ();
+
+#endif//DWARFLINT_DWARF_4_HH
diff --git a/dwarflint/dwarf_gnu.cc b/dwarflint/dwarf_gnu.cc
new file mode 100644 (file)
index 0000000..97f28d0
--- /dev/null
@@ -0,0 +1,117 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dwarf_version-imp.hh"
+#include "../libdw/dwarf.h"
+
+namespace
+{
+  struct dwarf_gnu_attributes
+    : public attribute_table
+  {
+    void unused (__attribute__ ((unused)) attribute const &attrib) const {}
+    dwarf_gnu_attributes ()
+    {
+      // It's rather hard to find any reference on some of the GNU
+      // extensions.  So we simply mark these with unused.
+
+      // The MIPS documentation claims that these are "apparently only
+      // output in DWARF1, not DWARF2".  I found nothing particular
+      // about how these are used.
+      unused (const_attribute (DW_AT_sf_names));
+      unused (const_attribute (DW_AT_src_info));
+      unused (const_attribute (DW_AT_mac_info));
+      unused (const_attribute (DW_AT_src_coords));
+      unused (const_attribute (DW_AT_body_begin));
+      unused (const_attribute (DW_AT_body_end));
+
+      add (flag_attribute (DW_AT_GNU_vector));
+
+      // xxx these are glass cl_GNU_mutexlistptr.  data4 and data8 are
+      // supposed to have this class.  So how do we smuggle this class
+      // to whatever DW_FORM_data4 and DW_FORM_data8 have in current
+      // version?  For now, just claim it's plain old constant.
+      // http://gcc.gnu.org/wiki/ThreadSafetyAnnotationsInDWARF
+      add (const_attribute (DW_AT_GNU_guarded_by));
+      add (const_attribute (DW_AT_GNU_pt_guarded_by));
+      add (const_attribute (DW_AT_GNU_guarded));
+      add (const_attribute (DW_AT_GNU_pt_guarded));
+      add (const_attribute (DW_AT_GNU_locks_excluded));
+      add (const_attribute (DW_AT_GNU_exclusive_locks_required));
+      add (const_attribute (DW_AT_GNU_shared_locks_required));
+
+      // Contains a shallower 8-byte signature of the type described
+      // in the type unit.  This is nominally a const_attribute, but
+      // we do the checking ourselves in form_allowed.
+      // http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo
+      add (const_attribute (DW_AT_GNU_odr_signature));
+
+      // http://gcc.gnu.org/wiki/TemplateParmsDwarf
+      add (string_attribute (DW_AT_GNU_template_name));
+
+      // GNU extensions for representation of call sites
+      // http://www.dwarfstd.org/ShowIssue.php?issue=100909.2
+      add (attribute (DW_AT_GNU_call_site_value, cl_exprloc));
+      add (attribute (DW_AT_GNU_call_site_data_value, cl_exprloc));
+      add (attribute (DW_AT_GNU_call_site_target, cl_exprloc));
+      add (attribute (DW_AT_GNU_call_site_target_clobbered, cl_exprloc));
+      add (flag_attribute (DW_AT_GNU_tail_call));
+      add (flag_attribute (DW_AT_GNU_all_tail_call_sites));
+      add (flag_attribute (DW_AT_GNU_all_call_sites));
+      add (flag_attribute (DW_AT_GNU_all_source_call_sites));
+    }
+  };
+
+  struct dwarf_gnu_ext_t
+    : public std_dwarf
+  {
+    dwarf_gnu_ext_t ()
+      : std_dwarf (dwarf_gnu_attributes (), form_table ())
+    {}
+
+    virtual bool
+    form_allowed (attribute const *attr, form const *form) const
+    {
+      // Without -gstrict-dwarf gcc allows usage of attributes from
+      // later versions. One strange case is DW_AT_ranges in version 2
+      // since that version doesn't actually define a rangelistptr
+      // class. So we just allow data4 or data8 here.
+      if (attr->name () == DW_AT_ranges)
+       {
+         form_width_t width = form->width (NULL);
+         return (form->classes ()[cl_constant]
+                 && (width == fw_4 || width == fw_8));
+       }
+
+      // upper_bound is allowed to also be a block (dwarf3 in dwarf2).
+      if (attr->name () == DW_AT_upper_bound)
+       return form->classes ()[cl_block];
+
+      if (attr->name () == DW_AT_GNU_odr_signature)
+       return form->classes ()[cl_constant] && form->width (NULL) == fw_8;
+      else
+       return std_dwarf::form_allowed (attr, form);
+    }
+  };
+}
+
+dwarf_version const *
+dwarf_gnu_ext ()
+{
+  static dwarf_gnu_ext_t dw;
+  return &dw;
+}
diff --git a/dwarflint/dwarf_gnu.hh b/dwarflint/dwarf_gnu.hh
new file mode 100644 (file)
index 0000000..1481c8c
--- /dev/null
@@ -0,0 +1,26 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_GNU_HH
+#define DWARFLINT_DWARF_GNU_HH
+
+#include "dwarf_version_i.hh"
+
+dwarf_version const *dwarf_gnu_ext ();
+
+#endif//DWARFLINT_DWARF_GNU_HH
diff --git a/dwarflint/dwarf_mips.cc b/dwarflint/dwarf_mips.cc
new file mode 100644 (file)
index 0000000..4386333
--- /dev/null
@@ -0,0 +1,88 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dwarf_version-imp.hh"
+#include "../libdw/dwarf.h"
+
+namespace
+{
+  struct dwarf_mips_attributes
+    : public attribute_table
+  {
+    void unused (__attribute__ ((unused)) attribute const &attrib) const {}
+    dwarf_mips_attributes ()
+    {
+      // Most of these are really just sketched, since we don't emit
+      // them anyway.  For those in need, the documentation is in
+      // mips_extensions.pdf that's installed by libdwarf-devel in
+      // Fedora.  According to that document, some forms were never
+      // even emitted.  Those are marked as unused and not added.
+      // Their class is arbitrarily chosen as cl_constant.
+
+      add (const_attribute (DW_AT_MIPS_fde));
+      unused (const_attribute (DW_AT_MIPS_loop_begin));
+      unused (const_attribute (DW_AT_MIPS_tail_loop_begin));
+      unused (const_attribute (DW_AT_MIPS_epilog_begin));
+      unused (const_attribute (DW_AT_MIPS_loop_unroll_factor));
+      unused (const_attribute (DW_AT_MIPS_software_pipeline_depth));
+      add (string_attribute (DW_AT_MIPS_linkage_name));
+
+      // [section 8.10] If DW_AT_MIPS_stride is present, the attribute
+      // contains a reference to a DIE which describes the location
+      // holding the stride, and the DW_AT_stride_size field of
+      // DW_TAG_array_type is ignored if present. The value of the
+      // stride is the number of 4 byte words between elements along
+      // that axis.
+      add (ref_attribute (DW_AT_MIPS_stride));
+
+      add (string_attribute (DW_AT_MIPS_abstract_name));
+
+      // xxx in addition, this is supposed to be CU-local reference,
+      // similarly to the DW_AT_sibling.  An opportunity to generalize
+      // sibling_form_suitable.
+      add (ref_attribute (DW_AT_MIPS_clone_origin));
+
+      add (flag_attribute (DW_AT_MIPS_has_inlines));
+
+      // The documentation is unclear on what form these should take.
+      // I'm making them the same as DW_AT_byte_stride in DWARF2, in
+      // hopes that that's what they are supposed to be.
+      add (const_attribute (DW_AT_MIPS_stride_byte));
+      add (const_attribute (DW_AT_MIPS_stride_elem));
+
+      add (ref_attribute (DW_AT_MIPS_ptr_dopetype));
+      add (ref_attribute (DW_AT_MIPS_allocatable_dopetype));
+      add (ref_attribute (DW_AT_MIPS_assumed_shape_dopetype));
+      add (flag_attribute (DW_AT_MIPS_assumed_size));
+    }
+  };
+
+  struct dwarf_mips_ext_t
+    : public std_dwarf
+  {
+    dwarf_mips_ext_t ()
+      : std_dwarf (dwarf_mips_attributes (), form_table ())
+    {}
+  };
+}
+
+dwarf_version const *
+dwarf_mips_ext ()
+{
+  static dwarf_mips_ext_t dw;
+  return &dw;
+}
diff --git a/dwarflint/dwarf_mips.hh b/dwarflint/dwarf_mips.hh
new file mode 100644 (file)
index 0000000..0ec678f
--- /dev/null
@@ -0,0 +1,26 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_MIPS_HH
+#define DWARFLINT_DWARF_MIPS_HH
+
+#include "dwarf_version_i.hh"
+
+dwarf_version const *dwarf_mips_ext ();
+
+#endif//DWARFLINT_DWARF_MIPS_HH
diff --git a/dwarflint/dwarf_version-imp.cc b/dwarflint/dwarf_version-imp.cc
new file mode 100644 (file)
index 0000000..b4b20b5
--- /dev/null
@@ -0,0 +1,69 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "dwarf_version-imp.hh"
+
+template <class T>
+void
+dwver_index_table<T>::add (T const &emt)
+{
+  _m_table.insert (std::make_pair (emt.name (), emt));
+}
+
+template <class T>
+T const *
+dwver_index_table<T>::get (int f) const
+{
+  typename _table_t::const_iterator it = _m_table.find (f);
+  if (it != _m_table.end ())
+    return &it->second;
+  else
+    return NULL;
+}
+
+template class dwver_index_table<form>;
+template class dwver_index_table<attribute>;
+
+offset_form::offset_form (int a_name, dw_class_set a_classes)
+  : form (a_name, a_classes, fw_offset, sc_value)
+{}
+
+address_form::address_form (int a_name, dw_class_set a_classes)
+  : form (a_name, a_classes, fw_address, sc_value)
+{}
+
+string_form::string_form (int a_name)
+  : preset_form<sc_string, cl_string> (a_name, fw_unknown)
+{}
+
+std_dwarf::std_dwarf (attribute_table const &attrtab,
+                     form_table const &formtab)
+  : _m_attrtab (attrtab)
+  , _m_formtab (formtab)
+{}
+
+form const *
+std_dwarf::get_form (int form_name) const
+{
+  return _m_formtab.get (form_name);
+}
+
+attribute const *
+std_dwarf::get_attribute (int attribute_name) const
+{
+  return _m_attrtab.get (attribute_name);
+}
diff --git a/dwarflint/dwarf_version-imp.hh b/dwarflint/dwarf_version-imp.hh
new file mode 100644 (file)
index 0000000..8c17084
--- /dev/null
@@ -0,0 +1,114 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_VERSION_IMP_HH
+#define DWARFLINT_DWARF_VERSION_IMP_HH
+
+#include "dwarf_version.hh"
+#include <map>
+
+template <class T>
+class dwver_index_table
+{
+  typedef std::map<int, T> _table_t;
+  _table_t _m_table;
+
+protected:
+  void add (T const &emt);
+
+public:
+  T const *get (int name) const;
+};
+
+typedef dwver_index_table<form> form_table;
+typedef dwver_index_table<attribute> attribute_table;
+
+template<storage_class_t StorClass, dw_class... Classes>
+struct preset_form
+  : public form
+{
+  preset_form (int a_name, form_width_t a_width,
+              form_bitness_t a_bitness = fb_any)
+    : form (a_name, dw_class_set (Classes...), a_width, StorClass, a_bitness)
+  {}
+};
+
+template<dw_class... Classes>
+struct preset_attribute
+  : public attribute
+{
+  preset_attribute (int a_name)
+    : attribute (a_name, dw_class_set (Classes...))
+  {}
+};
+
+\f
+struct offset_form
+  : public form
+{
+  offset_form (int a_name, dw_class_set a_classes);
+};
+
+struct address_form
+  : public form
+{
+  address_form (int a_name, dw_class_set a_classes);
+};
+
+struct string_form
+  : public preset_form<sc_string, cl_string>
+{
+  string_form (int a_name);
+};
+
+typedef preset_form<sc_block, cl_block> block_form;
+typedef preset_form<sc_value, cl_constant> const_form;
+typedef preset_form<sc_value, cl_reference> ref_form;
+typedef preset_form<sc_value, cl_flag> flag_form;
+
+typedef preset_attribute<cl_constant> const_attribute;
+typedef preset_attribute<cl_reference> ref_attribute;
+typedef preset_attribute<cl_address> addr_attribute;
+typedef preset_attribute<cl_string> string_attribute;
+typedef preset_attribute<cl_flag> flag_attribute;
+typedef preset_attribute<cl_block> block_attribute;
+
+// [DWARF 3, DWARF 4, section 2.19]: attributes that [...] specify a
+// property [...] that is an integer value, where the value may be
+// known during compilation or may be computed dynamically during
+// execution.
+typedef preset_attribute<cl_constant, cl_exprloc,
+                        cl_reference> dynval_attribute;
+
+typedef preset_attribute<cl_exprloc, cl_loclistptr> location_attribute;
+typedef preset_attribute<cl_exprloc, cl_reference> static_location_attribute;
+
+class std_dwarf
+  : public dwarf_version
+{
+  attribute_table const _m_attrtab;
+  form_table const _m_formtab;
+
+public:
+  std_dwarf (attribute_table const &attrtab,
+            form_table const &formtab);
+
+  form const *get_form (int form_name) const;
+  attribute const *get_attribute (int attribute_name) const;
+};
+
+#endif//DWARFLINT_DWARF_VERSION_IMP_HH
diff --git a/dwarflint/dwarf_version.cc b/dwarflint/dwarf_version.cc
new file mode 100644 (file)
index 0000000..57fd4b4
--- /dev/null
@@ -0,0 +1,293 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+// The tables here capture attribute/allowed forms depending on DWARF
+// version.  Apart from standardized DWARF formats, e.g. DWARF3+GNU is
+// a version of its own.
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <map>
+#include <cassert>
+#include <string.h>
+
+#include "../libdw/c++/dwarf"
+
+#include "dwarf_version.hh"
+#include "dwarf_2.hh"
+#include "dwarf_3.hh"
+#include "dwarf_4.hh"
+#include "dwarf_gnu.hh"
+#include "dwarf_mips.hh"
+#include "check_debug_info.hh"
+
+global_opt<void_option>
+  opt_nognu ("Don't use GNU extension.", "nognu");
+
+dw_class_set::dw_class_set (dw_class a, dw_class b, dw_class c,
+                           dw_class d, dw_class e)
+{
+#define ADD(V) if (V != max_dw_class) (*this)[V] = true
+  ADD (a);
+  ADD (b);
+  ADD (c);
+  ADD (d);
+  ADD (e);
+#undef ADD
+}
+
+form::form (int a_name, dw_class_set a_classes,
+           form_width_t a_width, storage_class_t a_storclass,
+           form_bitness_t a_bitness)
+  : _m_name (a_name)
+  , _m_classes (a_classes)
+  , _m_width (a_width)
+  , _m_storclass (a_storclass)
+  , _m_bitness (a_bitness)
+{}
+
+form::form (int a_name, dw_class_set a_classes,
+           form_width_special_t a_width, storage_class_t a_storclass,
+           form_bitness_t a_bitness)
+  : _m_name (a_name)
+  , _m_classes (a_classes)
+  , _m_width (a_width)
+  , _m_storclass (a_storclass)
+  , _m_bitness (a_bitness)
+{}
+
+dw_class
+dwarf_version::form_class (form const *form, attribute const *attribute) const
+{
+  assert (form != NULL);
+  assert (attribute != NULL);
+  dw_class_set result = form->classes ();
+  result &= attribute->classes ();
+  if (result.count () > 1)
+    {
+      dw_class ret = this->ambiguous_class (form, attribute, result);
+      assert (ret < max_dw_class);
+      assert (result[ret]);
+      return ret;
+    }
+  else if (result.count () == 1)
+    return static_cast<dw_class> (ffsl (result.to_ulong ()) - 1);
+  else
+    return max_dw_class;
+}
+
+form_width_t
+form::width (cu_head const *cu_head) const
+{
+  switch (_m_width)
+    {
+    case fw_offset:
+    case fw_address:
+      if (unlikely (cu_head == NULL))
+       return fw_unknown;
+      if (_m_width == fw_offset)
+       return static_cast<form_width_t> (cu_head->offset_size);
+      else
+       return static_cast<form_width_t> (cu_head->address_size);
+
+    default:
+      return static_cast<form_width_t> (_m_width);
+    }
+}
+
+std::ostream &
+operator << (std::ostream &os, form const &obj)
+{
+  return os << elfutils::dwarf::forms::identifier (obj.name ());
+}
+
+namespace
+{
+  dw_class_set
+  include_indirect (dw_class_set a_classes)
+  {
+    a_classes.set (cl_indirect);
+    return a_classes;
+  }
+}
+
+attribute::attribute (int a_name, dw_class_set const &a_classes)
+  : _m_name (a_name)
+  , _m_classes (include_indirect (a_classes))
+{}
+
+std::ostream &
+operator << (std::ostream &os, attribute const &obj)
+{
+  return os << elfutils::dwarf::attributes::identifier (obj.name ());
+}
+
+
+bool
+dwarf_version::form_allowed (int form) const
+{
+  return get_form (form) != NULL;
+}
+
+bool
+dwarf_version::form_allowed (attribute const *attr, form const *form) const
+{
+  dw_class_set const &attr_classes = attr->classes ();
+  dw_class_set const &form_classes = form->classes ();
+  return (attr_classes & form_classes).any ();
+}
+
+sibling_form_suitable_t
+sibling_form_suitable (dwarf_version const *ver, form const *form)
+{
+  if (!ver->form_allowed (ver->get_attribute (DW_AT_sibling), form))
+    return sfs_invalid;
+  else if (form->name () == DW_FORM_ref_addr)
+    return sfs_long;
+  else
+    return sfs_ok;
+}
+
+namespace
+{
+  class dwarf_version_union
+    : public dwarf_version
+  {
+    dwarf_version const *_m_source;
+    dwarf_version const *_m_extension;
+
+  public:
+    dwarf_version_union (dwarf_version const *source,
+                        dwarf_version const *extension)
+      : _m_source (source)
+      , _m_extension (extension)
+    {
+    }
+
+    template<class T>
+    T const *
+    lookfor (int name, T const*(dwarf_version::*getter) (int) const) const
+    {
+      if (T const *emt = (_m_extension->*getter) (name))
+       return emt;
+      else
+       return (_m_source->*getter) (name);
+    }
+
+    form const *
+    get_form (int form_name) const
+    {
+      return lookfor (form_name, &dwarf_version::get_form);
+    }
+
+    attribute const *
+    get_attribute (int attribute_name) const
+    {
+      return lookfor (attribute_name, &dwarf_version::get_attribute);
+    }
+
+    dw_class
+    ambiguous_class (form const *form,
+                    attribute const *attribute,
+                    dw_class_set const &candidates) const
+    {
+      dw_class ret = _m_extension->ambiguous_class (form, attribute, candidates);
+      if (ret == max_dw_class)
+       ret = _m_source->ambiguous_class (form, attribute, candidates);
+      return ret;
+    }
+
+    bool
+    form_allowed (attribute const *attr, form const *form) const
+    {
+      // In GNU mode any combination of new attribute/old form goes,
+      // in strict mode only the latest.
+      if (opt_nognu)
+       return _m_extension->form_allowed (attr, form);
+      else
+       return (_m_source->form_allowed (attr, form)
+               || _m_extension->form_allowed (attr, form));
+    }
+  };
+}
+
+dwarf_version const *
+dwarf_version::extend (dwarf_version const *source,
+                      dwarf_version const *extension)
+{
+  assert (source != NULL);
+  assert (extension != NULL);
+  // this leaks, but we don't really care.  These objects have to live
+  // the whole execution time anyway.
+  return new dwarf_version_union (source, extension);
+}
+
+namespace
+{
+  dwarf_version const *get_ext ()
+  {
+    // xxx The GNU toolchain commonly uses DW_AT_MIPS_linkage_name,
+    // which is part of the MIPS extensions.  So that's what we
+    // return.  I wonder how to solve this "right".  We cannot simply
+    // request DW_AT_producer/DW_AT_language values here, since we
+    // need the version to know how to read these attributes in the
+    // first place.
+
+    if (opt_nognu)
+      return dwarf_mips_ext ();
+    else
+      return dwarf_version::extend (dwarf_mips_ext (), dwarf_gnu_ext ());
+  }
+}
+
+dwarf_version const *
+dwarf_version::get (unsigned version)
+{
+  static dwarf_version const *ext = get_ext ();
+
+  switch (version)
+    {
+    case 2:
+      {
+       static dwarf_version const *dw = extend (dwarf_2 (), ext);
+       return dw;
+      }
+
+    case 3:
+      {
+       static dwarf_version const *dw = extend (dwarf_3 (), ext);
+       return dw;
+      }
+
+    case 4:
+      {
+       static dwarf_version const *dw = extend (dwarf_4 (), ext);
+       return dw;
+      }
+
+    default:
+      return NULL;
+    };
+}
+
+dwarf_version const *
+dwarf_version::get_latest ()
+{
+  return get (4);
+}
diff --git a/dwarflint/dwarf_version.hh b/dwarflint/dwarf_version.hh
new file mode 100644 (file)
index 0000000..177743a
--- /dev/null
@@ -0,0 +1,240 @@
+/* Dwarf version tables.
+
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_DWARF_VERSION_HH
+#define DWARFLINT_DWARF_VERSION_HH
+
+#include <bitset>
+#include <iosfwd>
+#include "check_debug_info_i.hh"
+#include "dwarf_version_i.hh"
+#include "option.hh"
+
+extern global_opt<void_option> opt_nognu;
+
+enum dw_class
+  {
+    cl_indirect,
+    cl_address,
+    cl_block,
+    cl_constant,
+    cl_exprloc,
+    cl_flag,
+    cl_reference,
+    cl_string,
+    cl_loclistptr,
+    cl_lineptr,
+    cl_macptr,
+    cl_rangelistptr,
+    max_dw_class
+  };
+
+class dw_class_set
+  : public std::bitset<max_dw_class>
+{
+public:
+  dw_class_set (dw_class a = max_dw_class, dw_class b = max_dw_class,
+               dw_class c = max_dw_class, dw_class d = max_dw_class,
+               dw_class e = max_dw_class);
+};
+
+enum form_width_t
+  {
+    fw_0 = 0,
+    fw_1 = 1,
+    fw_2 = 2,
+    fw_4 = 4,
+    fw_8 = 8,
+    fw_sleb,
+    fw_uleb,
+    fw_unknown,
+  };
+
+/// Special forms for use in DWARF tables.  These never leak out to
+/// the user of dwarf_version.
+enum form_width_special_t
+  {
+    fw_offset = fw_unknown + 1,
+    fw_address,
+  };
+
+enum storage_class_t
+  {
+    sc_value,
+    sc_block,
+    sc_string,
+  };
+
+enum form_bitness_t
+  {
+    fb_any, ///< Form is allowed in all CUs
+    fb_32,  ///< Form is allowed only in 32-bit CUs
+    fb_64,  ///< Form is allowed only in 64-bit CUs
+  };
+
+class form
+{
+  int const _m_name;
+  dw_class_set const _m_classes;
+  int const _m_width;
+  storage_class_t const _m_storclass;
+  form_bitness_t _m_bitness;
+
+public:
+  form (int name, dw_class_set classes,
+       form_width_t width, storage_class_t storclass,
+       form_bitness_t bitness = fb_any);
+
+  form (int name, dw_class_set classes,
+       form_width_special_t width, storage_class_t storclass,
+       form_bitness_t bitness = fb_any);
+
+  int
+  name () const
+  {
+    return _m_name;
+  }
+
+  /// Answer set of DWARF classes that this form can have.
+  dw_class_set const &
+  classes () const
+  {
+    return _m_classes;
+  }
+
+  /// Return width of data stored with given form.  CU may be NULL if
+  /// you are sure that the form size doesn't depend on bitness of
+  /// address_size or offset_size.
+  ///
+  /// Forms for which width makes no sense (namely those in the
+  /// storage class of sc_string) get fw_unknown.  Unknown forms get
+  /// an assert.
+  ///
+  /// Return value is never fw_offset or fw_address.  These get
+  /// resolved to fw_4 or fw_8 depending on corresponding value in
+  /// CU_HEAD.
+  form_width_t width (cu_head const *cu_head) const;
+
+  /// Return storage class of given form.  Closely related to width.
+  storage_class_t
+  storage_class () const
+  {
+    return _m_storclass;
+  }
+
+  form_bitness_t
+  bitness () const
+  {
+    return _m_bitness;
+  }
+};
+std::ostream &operator << (std::ostream &os, form const &obj);
+
+
+class attribute
+{
+  int const _m_name;
+  dw_class_set const _m_classes;
+
+public:
+  /// NB this ctor automatically adds cl_indirect to a_classes.
+  attribute (int a_name, dw_class_set const &a_classes);
+
+  int
+  name () const
+  {
+    return _m_name;
+  }
+
+  /// Answer set of DWARF classes that this form can have.
+  dw_class_set const &
+  classes () const
+  {
+    return _m_classes;
+  }
+};
+std::ostream &operator << (std::ostream &os, attribute const &obj);
+
+class dwarf_version
+{
+public:
+  /// Return form object for given form name.  Return NULL for unknown
+  /// forms.
+  virtual form const *get_form (int form_name) const = 0;
+
+  /// Return attribute object for given attribute name.  Return NULL
+  /// for unknown attributes;
+  virtual attribute const *get_attribute (int attribute_name) const = 0;
+
+  /// If more than one class ends up as a candidate after the request
+  /// to form_class, this function is called to resolve the ambiguity.
+  virtual dw_class
+  ambiguous_class (__attribute__ ((unused)) form const *form,
+                  __attribute__ ((unused)) attribute const *attribute,
+                  __attribute__ ((unused)) dw_class_set const &candidates)
+    const
+  {
+    return max_dw_class; // = we don't know.  This will assert back in caller.
+  }
+
+  /// Shortcut for get_form (form_name) != NULL.
+  bool form_allowed (int form_name) const;
+
+  /// Figure out whether, in given DWARF version, given attribute is
+  /// allowed to have given form.
+  virtual bool form_allowed (attribute const *attr, form const *form) const
+    __attribute__ ((nonnull (1, 2)));
+
+  /// Answer a class of FORM given ATTRIBUTE as a context.  If there's
+  /// exactly one candidate class, that's the one answered.  If
+  /// there's more, ambiguous_class is called to resolve the
+  /// ambiguity.  If there's no candidate, then the request is
+  /// invalid, you must validate the form via form_allowed before
+  /// calling this.
+  dw_class form_class (form const *form, attribute const *attribute) const;
+
+
+  /// Return dwarf_version object for given DWARF version.
+  static dwarf_version const *get (unsigned version)
+    __attribute__ ((pure));
+
+  /// Return dwarf_version object for latest supported DWARF version.
+  static dwarf_version const *get_latest ()
+    __attribute__ ((pure));
+
+  /// Return dwarf_version that represents SOURCE extended with
+  /// EXTENSION.  Currently this probably has no use, but one obvious
+  /// candidate usage is representing GNU extensions over core DWARF.
+  /// Extension can contain overrides of the source dwarf_version
+  /// object, and these overrides take precedence.
+  static dwarf_version const *extend (dwarf_version const *source,
+                                     dwarf_version const *extension);
+};
+
+/// Check that the form is suitable for the DW_AT_sibling attribute.
+enum sibling_form_suitable_t
+  {
+    sfs_ok,      ///< This form is OK for DW_AT_sibling
+    sfs_long,    ///< Global reference form, unnecessary for DW_AT_sibling
+    sfs_invalid, ///< This form isn't allowed at DW_AT_sibling
+  };
+sibling_form_suitable_t sibling_form_suitable (dwarf_version const *ver,
+                                              form const *form)
+  __attribute__ ((nonnull (1, 2)));
+
+#endif//DWARFLINT_DWARF_VERSION_HH
diff --git a/dwarflint/dwarf_version_i.hh b/dwarflint/dwarf_version_i.hh
new file mode 100644 (file)
index 0000000..b1a54b5
--- /dev/null
@@ -0,0 +1,3 @@
+class form;
+class attribute;
+class dwarf_version;
diff --git a/dwarflint/dwarflint.cc b/dwarflint/dwarflint.cc
new file mode 100644 (file)
index 0000000..8067bf4
--- /dev/null
@@ -0,0 +1,245 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2008,2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "dwarflint.hh"
+#include "messages.hh"
+#include "checks.hh"
+#include "check_registrar.hh"
+#include "files.hh"
+#include "main.hh"
+
+#include <fcntl.h>
+#include <cstring>
+#include <cerrno>
+#include <stdexcept>
+
+std::ostream &
+operator << (std::ostream &o, checkstack const &stack)
+{
+  o << "{";
+  for (checkstack::const_iterator it = stack.begin ();
+       it != stack.end (); ++it)
+    {
+      if (it != stack.begin ())
+       o << ',';
+      o << (*it)->name ();
+    }
+  o << "}";
+  return o;
+}
+
+void
+main_check_registrar::run (dwarflint &lint)
+{
+  for (const_iterator it = begin (); it != end (); ++it)
+    if ((*it)->descriptor ()->schedule ())
+      {
+       checkstack stack;
+       (*it)->run (stack, lint);
+      }
+}
+
+dwarflint::dwarflint (char const *a_fname, checkrules const &a_rules)
+  : _m_fname (a_fname)
+  , _m_fd (files::open (_m_fname))
+  , _m_rules (a_rules)
+{
+  main_registrar ()->run (*this);
+}
+
+dwarflint::~dwarflint ()
+{
+  if (close (_m_fd) < 0)
+    // Not that we can do anything about it...
+    wr_error () << "Couldn't close the file " << _m_fname << ": "
+               << strerror (errno) << "." << std::endl;
+  for (check_map::const_iterator it = _m_checks.begin ();
+       it != _m_checks.end (); ++it)
+    delete it->second;
+}
+
+void *const dwarflint::marker = (void *)-1;
+
+void *
+dwarflint::find_check (void const *key)
+{
+  check_map::const_iterator it = _m_checks.find (key);
+
+  if (it != _m_checks.end ())
+    {
+      void *c = it->second;
+
+      // We already tried to do the check, but failed.
+      if (c == NULL)
+       throw check_base::failed ();
+      else
+       // Recursive dependency!
+       assert (c != marker);
+
+      return c;
+    }
+
+  return NULL;
+}
+
+main_check_registrar *
+dwarflint::main_registrar ()
+{
+  static main_check_registrar inst;
+  return &inst;
+}
+
+namespace
+{
+  bool
+  be_verbose ()
+  {
+    // We can hopefully assume that the option doesn't change during
+    // execution, so we can simply cache it this was.
+    static bool be_verbose = opt_list_checks.value () == "full";
+    return be_verbose;
+  }
+
+  template <class T>
+  void
+  list_part_checks (T const &descriptors)
+  {
+    for (typename T::const_iterator it = descriptors.begin ();
+        it != descriptors.end (); ++it)
+      if (!(*it)->hidden ())
+       (*it)->list (be_verbose ());
+  }
+}
+
+void
+dwarflint::list_checks ()
+{
+  list_part_checks (dwarflint::main_registrar ()->get_descriptors ());
+
+  if (!be_verbose ())
+    std::cout
+      << "Use --list-checks=full to get more detailed description."
+      << std::endl;
+}
+
+static global_opt<void_option> show_progress
+  ("Print out checks as they are performed, their context and result.",
+   "show-progress");
+
+namespace
+{
+  struct reporter
+  {
+    checkstack const &stack;
+    checkdescriptor const &cd;
+
+    reporter (checkstack const &s, checkdescriptor const &a_cd);
+    void operator () (char const *what, bool ext = false);
+  };
+
+  reporter::reporter (checkstack const &s, checkdescriptor const &a_cd)
+    : stack (s)
+    , cd (a_cd)
+  {
+    (*this) ("...", true);
+  }
+
+  void
+  reporter::operator () (char const *what, bool ext)
+  {
+    if (!show_progress)
+      return;
+
+    if (false)
+      for (size_t i = 0; i < stack.size (); ++i)
+       std::cout << ' ';
+
+    std::cout << cd.name () << ' ' << what;
+    if (ext)
+      std::cout << ' ' << cd.groups () << ' ' << stack;
+    std::cout << std::endl;
+  }
+}
+
+void *
+dwarflint::dispatch_check (checkstack &stack,
+                          checkdescriptor const &cd,
+                          void const *key,
+                          check_base *(* create) (checkstack &, dwarflint &))
+{
+  // Put a marker there indicating that we are trying to satisfy
+  // that dependency.
+  bool inserted
+    = _m_checks.insert (std::make_pair (key, (check_base *)marker)).second;
+  assert (inserted || !"duplicate key");
+
+#define FAIL                                   \
+  /* Put the anchor in the table.  */          \
+    _m_checks[key] = NULL;                     \
+    report ("FAIL")
+
+  reporter report (stack, cd);
+  try
+    {
+      stack.push_back (&cd);
+      popper p (stack);
+
+      if (!_m_rules.should_check (stack))
+       throw check_base::unscheduled ();
+
+      // Now do the check.
+      check_base *c = create (stack, *this);
+
+      // On success, put the actual check object there instead of the
+      // marker.
+      _m_checks[key] = c;
+      report ("done");
+      return c;
+    }
+  catch (check_base::unscheduled &e)
+    {
+      report ("skipped");
+      _m_checks.erase (key);
+      throw;
+    }
+  catch (check_base::failed &e)
+    {
+      // We can assume that the check emitted error message.
+      FAIL;
+      throw;
+    }
+  catch (std::exception &e)
+    {
+      wr_error () << "A check failed: " << (cd.name () ?: "(nil)") << ": "
+                 << e.what () << std::endl;
+      FAIL;
+      throw check_base::failed ();
+    }
+  catch (...)
+    {
+      wr_error () << "A check failed: " << (cd.name () ?: "(nil)") << "."
+                 << std::endl;
+      FAIL;
+      throw check_base::failed ();
+    }
+
+#undef FAIL
+}
diff --git a/dwarflint/dwarflint.hh b/dwarflint/dwarflint.hh
new file mode 100644 (file)
index 0000000..6b82883
--- /dev/null
@@ -0,0 +1,136 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_HH
+#define DWARFLINT_HH
+
+#include <map>
+#include <vector>
+#include <stdexcept>
+#include <iosfwd>
+
+#include "../libelf/libelf.h"
+#include "checks_i.hh"
+#include "checkdescriptor_i.hh"
+#include "checkrule.hh"
+#include "check_registrar.hh"
+#include "dwarflint_i.hh"
+#include "highlevel_check_i.hh"
+
+// Classes for full-blown check passes.
+struct main_check_item
+{
+  virtual checkdescriptor const *descriptor () const = 0;
+  virtual ~main_check_item () {}
+  virtual void run (checkstack &stack, dwarflint &lint) = 0;
+};
+
+class main_check_registrar
+  : public check_registrar_T<main_check_item>
+{
+public:
+  friend class dwarflint;
+  void run (dwarflint &lint);
+};
+
+class checkstack
+  : public std::vector <checkdescriptor const *>
+{};
+std::ostream &operator << (std::ostream &o, checkstack const &stack);
+
+
+class dwarflint
+{
+  typedef std::map <void const *, class check_base *> check_map;
+  check_map _m_checks;
+  char const *_m_fname;
+  int _m_fd;
+  checkrules const &_m_rules;
+
+  static void *const marker;
+
+  // Return a pointer to check, or NULL if the check hasn't been done
+  // yet.  Throws check_base::failed if the check was requested
+  // earlier but failed, or aborts program via assertion if recursion
+  // was detected.
+  void *find_check (void const *key);
+
+  template <class T>
+  static check_base *
+  create_check_object (checkstack &stack, dwarflint &lint)
+  {
+    return new T (stack, lint);
+  }
+
+  void *dispatch_check (checkstack &stack,
+                       checkdescriptor const &cd,
+                       void const *key,
+                       check_base *(* create) (checkstack &, dwarflint &));
+
+public:
+  dwarflint (char const *fname, checkrules const &rules);
+  ~dwarflint ();
+  int fd () { return _m_fd; }
+  char const *fname () { return _m_fname; }
+
+  template <class T>
+  T *
+  check (checkstack &stack)
+  {
+    void const *key = T::key ();
+    T *c = static_cast <T *> (find_check (key));
+    checkdescriptor const &cd = *T::descriptor ();
+
+    if (c == NULL)
+      c = (T *)dispatch_check (stack, cd, key, &create_check_object<T>);
+
+    return c;
+  }
+
+  template <class T>
+  T *
+  check (checkstack &stack, T *)
+  {
+    return check<T> (stack);
+  }
+
+  template <class T>
+  T *toplev_check (checkstack &stack, T *fake = NULL);
+
+  template <class T>
+  T *
+  check_if (bool whether, checkstack &stack,
+           __attribute__ ((unused)) T *fake = NULL)
+  {
+    if (whether)
+      return check<T> (stack);
+    else
+      return NULL;
+  }
+
+  checkrules const &
+  rules () const
+  {
+    return _m_rules;
+  }
+
+  static main_check_registrar *main_registrar ();
+
+  static void list_checks ();
+};
+
+#endif//DWARFLINT_HH
diff --git a/dwarflint/dwarflint_i.hh b/dwarflint/dwarflint_i.hh
new file mode 100644 (file)
index 0000000..9769d6b
--- /dev/null
@@ -0,0 +1,3 @@
+class checkstack;
+class dwarflint;
+class main_check_registrar;
diff --git a/dwarflint/elf_file.hh b/dwarflint/elf_file.hh
new file mode 100644 (file)
index 0000000..34fb7c4
--- /dev/null
@@ -0,0 +1,62 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2008, 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_ELF_FILE_HH
+#define DWARFLINT_ELF_FILE_HH
+
+#include "../libdw/libdw.h"
+#include "../libebl/libebl.h"
+#include "reloc.hh"
+
+struct sec
+{
+  GElf_Shdr shdr;
+  struct relocation_data rel;
+  Elf_Scn *scn;
+  const char *name;
+
+  Elf_Data *data;      /* May be NULL if data in this section are
+                          missing or not substantial.  */
+  enum section_id id;
+
+  sec ()
+    : scn (NULL)
+    , name (NULL)
+    , data (NULL)
+    , id (sec_invalid)
+  {}
+};
+
+struct elf_file
+{
+  GElf_Ehdr ehdr;      /* Header of underlying Elf.  */
+  Elf *elf;
+  Ebl *ebl;
+
+  struct sec *sec;     /* Array of sections.  */
+  size_t size;
+  size_t alloc;
+
+  /* Pointers into SEC above.  Maps section_id to section.  */
+  struct sec *debugsec[count_debuginfo_sections];
+
+  bool addr_64;        /* True if it's 64-bit Elf.  */
+  bool other_byte_order; /* True if the file has a byte order
+                           different from the host.  */
+};
+
+#endif/*DWARFLINT_ELF_FILE_HH*/
diff --git a/dwarflint/elf_file_i.hh b/dwarflint/elf_file_i.hh
new file mode 100644 (file)
index 0000000..f1daaf0
--- /dev/null
@@ -0,0 +1,4 @@
+struct sec;
+struct elf_file;
+struct abbrev_table;
+struct cu;
diff --git a/dwarflint/expected-at.cc b/dwarflint/expected-at.cc
new file mode 100644 (file)
index 0000000..2811130
--- /dev/null
@@ -0,0 +1,868 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2009, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+   Written by Petr Machata <pmachata@redhat.com>, 2009.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include "expected.hh"
+#include "../libdw/dwarf.h"
+
+expected_at_map::expected_at_map ()
+{
+  std::set <int> at_set_decl;
+  at_set_decl.insert (DW_AT_decl_column);
+  at_set_decl.insert (DW_AT_decl_file);
+  at_set_decl.insert (DW_AT_decl_line);
+
+  std::set <int> at_linkage_name;
+  at_linkage_name.insert (DW_AT_MIPS_linkage_name);
+  at_linkage_name.insert (DW_AT_linkage_name);
+
+  m_map [DW_TAG_access_declaration]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map[DW_TAG_array_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_bit_stride)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_ordering)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    .optional (DW_AT_GNU_vector)
+    ;
+
+  m_map [DW_TAG_base_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_binary_scale)
+    .optional (DW_AT_bit_offset)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_bit_offset)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_decimal_scale)
+    .optional (DW_AT_decimal_sign)
+    .optional (DW_AT_description)
+    .optional (DW_AT_digit_count)
+    .optional (DW_AT_encoding)
+    .optional (DW_AT_endianity)
+    .optional (DW_AT_name)
+    .optional (DW_AT_picture_string)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_small)
+    ;
+
+  m_map [DW_TAG_catch_block]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_class_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_signature)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_visibility)
+    .optional (DW_AT_containing_type) // XXX added to reflect reality
+    ;
+
+  m_map [DW_TAG_common_block]
+    .optional (at_set_decl)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (at_linkage_name)
+    .optional (DW_AT_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_common_inclusion]
+    .optional (at_set_decl)
+    .optional (DW_AT_common_reference)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_compile_unit]
+    .optional (DW_AT_base_types)
+    .optional (DW_AT_comp_dir)
+    .optional (DW_AT_identifier_case)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_language)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_macro_info)
+    .optional (DW_AT_main_subprogram)
+    .optional (DW_AT_name)
+    .optional (DW_AT_producer)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_stmt_list)
+    .optional (DW_AT_use_UTF8)
+    .optional (DW_AT_entry_pc) // XXX added to reflect reality
+    ;
+
+  m_map [DW_TAG_condition]
+    .optional (at_set_decl)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_const_type]
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_constant]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_const_value)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_endianity)
+    .optional (DW_AT_external)
+    .optional (at_linkage_name)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_dwarf_procedure]
+    .optional (DW_AT_location)
+    ;
+
+  m_map [DW_TAG_entry_point]
+    .optional (at_set_decl)
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_description)
+    .optional (DW_AT_frame_base)
+    .optional (at_linkage_name)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_name)
+    .optional (DW_AT_return_addr)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_static_link)
+    .optional (DW_AT_type)
+    .optional (DW_AT_GNU_all_tail_call_sites)
+    .optional (DW_AT_GNU_all_call_sites)
+    .optional (DW_AT_GNU_all_source_call_sites)
+    ;
+
+  m_map [DW_TAG_enumeration_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_bit_stride)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_byte_stride)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_signature)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    .optional (at_linkage_name) // GNU extension for anonymous typedef enums.
+    ;
+
+  m_map [DW_TAG_enumerator]
+    .optional (at_set_decl)
+    .optional (DW_AT_const_value)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_file_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_formal_parameter]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_artificial)
+    .optional (DW_AT_const_value)
+    .optional (DW_AT_default_value)
+    .optional (DW_AT_description)
+    .optional (DW_AT_endianity)
+    .optional (DW_AT_is_optional)
+    .optional (DW_AT_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_variable_parameter)
+    ;
+
+  m_map [DW_TAG_friend]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_friend)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_imported_declaration]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_description)
+    .optional (DW_AT_import)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    ;
+
+  m_map [DW_TAG_imported_module]
+    .optional (at_set_decl)
+    .optional (DW_AT_import)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    ;
+
+  m_map [DW_TAG_imported_unit]
+    .required (DW_AT_import)
+    ;
+
+  m_map [DW_TAG_inheritance]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_data_member_location)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_virtuality)
+    ;
+
+  m_map [DW_TAG_inlined_subroutine]
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_call_column)
+    .optional (DW_AT_call_file)
+    .optional (DW_AT_call_line)
+    .optional (DW_AT_const_expr)
+    .optional (DW_AT_entry_pc)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_return_addr)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_trampoline)
+    ;
+
+  m_map [DW_TAG_interface_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    ;
+
+  m_map [DW_TAG_label]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_description)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_name)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_lexical_block]
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_description)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_name)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    ;
+
+  // At one time gcc did emit at_linkage_name for members, but that
+  // has been corrected:
+  // http://gcc.gnu.org/ml/gcc-patches/2010-06/msg01713.html
+  m_map [DW_TAG_member]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_bit_offset)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_bit_offset)
+    .optional (DW_AT_data_member_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_mutable)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_module]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_entry_pc)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_name)
+    .optional (DW_AT_priority)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_namelist]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_namelist_item]
+    .optional (at_set_decl)
+    .optional (DW_AT_namelist_item)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_namespace]
+    .optional (at_set_decl)
+    .optional (DW_AT_description)
+    .optional (DW_AT_extension)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    ;
+
+  m_map [DW_TAG_packed_type]
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_partial_unit]
+    .optional (DW_AT_base_types)
+    .optional (DW_AT_comp_dir)
+    .optional (DW_AT_description)
+    .optional (DW_AT_identifier_case)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_language)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_macro_info)
+    .optional (DW_AT_name)
+    .optional (DW_AT_producer)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_stmt_list)
+    .optional (DW_AT_use_UTF8)
+    ;
+
+  m_map [DW_TAG_pointer_type]
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_byte_size) // XXX added to reflect reality
+    ;
+
+  m_map [DW_TAG_ptr_to_member_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_containing_type)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_use_location)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_reference_type]
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_byte_size) // XXX added to reflect reality
+    ;
+
+  m_map [DW_TAG_restrict_type]
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_rvalue_reference_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_set_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_shared_type]
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_count)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_string_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_string_length)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_structure_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_signature)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_visibility)
+    .optional (DW_AT_containing_type) // XXX added to reflect reality
+    .optional (at_linkage_name) // GNU extension for anonymous typedef structs.
+    ;
+
+  m_map [DW_TAG_subprogram]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_artificial)
+    .optional (DW_AT_calling_convention)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_elemental)
+    .optional (DW_AT_entry_pc)
+    .optional (DW_AT_explicit)
+    .optional (DW_AT_external)
+    .optional (DW_AT_frame_base)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_inline)
+    .optional (at_linkage_name)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_name)
+    .optional (DW_AT_object_pointer)
+    .optional (DW_AT_prototyped)
+    .optional (DW_AT_pure)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_recursive)
+    .optional (DW_AT_return_addr)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_static_link)
+    .optional (DW_AT_trampoline)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    .optional (DW_AT_virtuality)
+    .optional (DW_AT_vtable_elem_location)
+    .optional (DW_AT_containing_type) // XXX added to reflect reality
+    .optional (DW_AT_GNU_all_tail_call_sites)
+    .optional (DW_AT_GNU_all_call_sites)
+    .optional (DW_AT_GNU_all_source_call_sites)
+    ;
+
+  m_map [DW_TAG_subrange_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_bit_stride)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_byte_stride)
+    .optional (DW_AT_count)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_lower_bound)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_threads_scaled)
+    .optional (DW_AT_type)
+    .optional (DW_AT_upper_bound)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_subroutine_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_prototyped)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_template_alias]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_signature)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_template_type_parameter]
+    .optional (at_set_decl)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_template_value_parameter ]
+    .optional (at_set_decl)
+    .optional (DW_AT_const_value)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_thrown_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_try_block]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_typedef]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  m_map [DW_TAG_type_unit]
+    .optional (DW_AT_language)
+    ;
+
+  m_map [DW_TAG_union_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_bit_size)
+    .optional (DW_AT_byte_size)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_signature)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_visibility)
+    .optional (at_linkage_name) // GNU extension for anonymous typedef unions.
+    ;
+
+  m_map [DW_TAG_unspecified_parameters]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_artificial)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_unspecified_type]
+    .optional (at_set_decl)
+    .optional (DW_AT_description)
+    .optional (DW_AT_name)
+    ;
+
+  m_map [DW_TAG_variable]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_const_expr)
+    .optional (DW_AT_const_value)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_description)
+    .optional (DW_AT_endianity)
+    .optional (DW_AT_external)
+    .optional (at_linkage_name)
+    .optional (DW_AT_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_specification)
+    .optional (DW_AT_start_scope)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    .optional (DW_AT_artificial) // XXX added to reflect reality
+    ;
+
+  m_map [DW_TAG_variant]
+    .optional (at_set_decl)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_discr_list)
+    .optional (DW_AT_discr_value)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_variant_part]
+    .optional (at_set_decl)
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_discr)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_volatile_type]
+    .optional (DW_AT_allocated)
+    .optional (DW_AT_associated)
+    .optional (DW_AT_data_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_with_stmt]
+    .optional (DW_AT_accessibility)
+    .optional (DW_AT_address_class)
+    .optional (DW_AT_declaration)
+    .optional (DW_AT_high_pc)
+    .optional (DW_AT_location)
+    .optional (DW_AT_low_pc)
+    .optional (DW_AT_ranges)
+    .optional (DW_AT_segment)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    .optional (DW_AT_visibility)
+    ;
+
+  // http://gcc.gnu.org/wiki/summit2010?action=AttachFile&do=get&target=jelinek.pdf
+  m_map [DW_TAG_GNU_call_site]
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_call_column)
+    .optional (DW_AT_call_file)
+    .optional (DW_AT_call_line)
+    .optional (DW_AT_GNU_call_site_target)
+    .optional (DW_AT_GNU_call_site_target_clobbered)
+    .required (DW_AT_low_pc)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_GNU_tail_call)
+    .optional (DW_AT_type)
+    ;
+
+  m_map [DW_TAG_GNU_call_site_parameter]
+    .optional (DW_AT_abstract_origin)
+    .optional (DW_AT_GNU_call_site_data_value)
+    .optional (DW_AT_GNU_call_site_value)
+    .optional (DW_AT_data_location)
+    .required (DW_AT_location)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    .optional (DW_AT_type)
+    ;
+
+  // http://gcc.gnu.org/wiki/TemplateParmsDwarf
+  m_map [DW_TAG_GNU_template_template_param]
+    .required (DW_AT_name)
+    .required (DW_AT_GNU_template_name)
+    ;
+
+  m_map [DW_TAG_GNU_template_parameter_pack]
+    .optional (at_set_decl)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    ;
+
+  m_map [DW_TAG_GNU_formal_parameter_pack]
+    .optional (at_set_decl)
+    .optional (DW_AT_name)
+    .optional (DW_AT_sibling)
+    ;
+
+}
diff --git a/dwarflint/expected.hh b/dwarflint/expected.hh
new file mode 100644 (file)
index 0000000..cf1b3c5
--- /dev/null
@@ -0,0 +1,102 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2009 Red Hat, Inc.
+   This file is part of elfutils.
+   Written by Petr Machata <pmachata@redhat.com>, 2009.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <map>
+#include <set>
+#include <stdexcept>
+#include <sstream>
+#include <cassert>
+
+#include "../libdw/c++/dwarf"
+
+enum optionality
+{
+  opt_optional = 0,    // may or may not be present
+  opt_required,                // bogus if missing
+  opt_expected,                // suspicious if missing
+};
+
+template <class T>
+std::string
+string_of (T x)
+{
+  std::ostringstream o;
+  o << x;
+  return o.str();
+}
+
+struct expected_set
+{
+  typedef std::map <int, optionality> expectation_map;
+
+private:
+  expectation_map m_map;
+
+public:
+#define DEF_FILLER(WHAT)                                               \
+  expected_set &WHAT (int attribute)                                   \
+  {                                                                    \
+    assert (m_map.find (attribute) == m_map.end ());                   \
+    m_map.insert (std::make_pair (attribute, opt_##WHAT));             \
+    return *this;                                                      \
+  }                                                                    \
+  expected_set &WHAT (std::set <int> const &attributes)                        \
+  {                                                                    \
+    for (std::set <int>::const_iterator it = attributes.begin ();      \
+        it != attributes.end (); ++it)                                 \
+      WHAT (*it);                                                      \
+    return *this;                                                      \
+  }
+
+  DEF_FILLER (required)
+  DEF_FILLER (expected)
+  DEF_FILLER (optional)
+#undef DEF_FILLER
+
+  expectation_map const &map () const
+  {
+    return m_map;
+  }
+};
+
+class expected_map
+{
+  typedef std::map <int, expected_set> expected_map_t;
+
+protected:
+  expected_map_t m_map;
+  expected_map () {}
+
+public:
+  expected_set::expectation_map const &map (int tag) const
+  {
+    expected_map_t::const_iterator it = m_map.find (tag);
+    if (it == m_map.end ())
+      throw std::runtime_error ("Unknown tag "
+                               + elfutils::dwarf::tags::identifier (tag));
+    return it->second.map ();
+  }
+};
+
+struct expected_at_map
+  : public expected_map
+{
+  expected_at_map ();
+};
diff --git a/dwarflint/files.cc b/dwarflint/files.cc
new file mode 100644 (file)
index 0000000..fb74a0e
--- /dev/null
@@ -0,0 +1,138 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "files.hh"
+#include "messages.hh"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sstream>
+#include <cstring>
+#include <cerrno>
+#include <stdexcept>
+
+namespace
+{
+  inline bool failed (void *ptr) { return ptr == NULL; }
+  inline bool failed (int i) { return i < 0; }
+
+  template <class T>
+  inline T
+  throw_if_failed (T x, char const *msg,
+                  char const *(*errmsgcb) (int) = NULL)
+  {
+    if (unlikely (failed (x)))
+      {
+       std::stringstream ss;
+       ss << msg;
+       if (errmsgcb != NULL)
+         ss << ": " << errmsgcb (-1);
+       throw std::runtime_error (ss.str ());
+      }
+    return x;
+  }
+
+  char const *
+  mystrerror (int i)
+  {
+    if (i == -1)
+      i = errno;
+    return strerror (i);
+  }
+}
+
+int
+files::open (char const *fname)
+{
+  int fd = ::open (fname, O_RDONLY);
+  if (fd == -1)
+    {
+      std::stringstream ss;
+      ss << "Cannot open input file: " << strerror (errno) << ".";
+      throw std::runtime_error (ss.str ());
+    }
+
+  return fd;
+}
+
+Dwfl *
+files::open_dwfl ()
+{
+  static class my_callbacks
+    : public Dwfl_Callbacks
+  {
+    // Stub libdwfl callback, only the ELF handle already open is ever used.
+    static int
+    find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)),
+                      void **userdata __attribute__ ((unused)),
+                      const char *modname __attribute__ ((unused)),
+                      Dwarf_Addr base __attribute__ ((unused)),
+                      const char *file_name __attribute__ ((unused)),
+                      const char *debuglink_file __attribute__ ((unused)),
+                      GElf_Word debuglink_crc __attribute__ ((unused)),
+                      char **debuginfo_file_name __attribute__ ((unused)))
+    {
+      return -1;
+    }
+
+  public:
+    my_callbacks ()
+    {
+      section_address = dwfl_offline_section_address;
+      find_debuginfo = find_no_debuginfo;
+    }
+  } cbs;
+
+  return throw_if_failed (dwfl_begin (&cbs),
+                         "Couldn't initialize DWFL");
+}
+
+Dwarf *
+files::open_dwarf (Dwfl *dwfl, char const *fname, int fd)
+{
+  dwfl_report_begin (dwfl);
+
+  // Dup FD for dwfl to consume.
+  int dwfl_fd
+    = throw_if_failed (dup (fd), "Error: dup", mystrerror);
+
+  Dwfl_Module *mod
+    = throw_if_failed (dwfl_report_offline (dwfl, fname, fname, dwfl_fd),
+                      "Couldn't add DWFL module", dwfl_errmsg);
+  dwfl_report_end (dwfl, NULL, NULL);
+  Dwarf_Addr bias;
+  throw_if_failed (dwfl_module_getelf (mod, &bias),
+                  "Couldn't open ELF.", dwfl_errmsg);
+  return throw_if_failed (dwfl_module_getdwarf (mod, &bias),
+                         "Couldn't obtain DWARF descriptor", dwfl_errmsg);
+}
+
+elfutils::dwarf
+files::open_dwarf (Dwarf *dw)
+  try
+    {
+      return dw;
+    }
+  catch (...)
+    {
+      throw std::runtime_error
+       ("Couldn't initialize high-level DWARF descriptor");
+    }
diff --git a/dwarflint/files.hh b/dwarflint/files.hh
new file mode 100644 (file)
index 0000000..a7baa3b
--- /dev/null
@@ -0,0 +1,39 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DWARFLINT_FILES_H_
+#define _DWARFLINT_FILES_H_
+
+#include "../libdwfl/libdwfl.h"
+#include "../libdw/c++/dwarf"
+
+// The functions in this module do their own error handling, and throw
+// std::runtime_error with descriptive error message on error.
+namespace files
+{
+  int open (char const *fname);
+
+  Dwfl *open_dwfl ()
+    __attribute__ ((nonnull, malloc));
+
+  Dwarf *open_dwarf (Dwfl *dwfl, char const *fname, int fd)
+    __attribute__ ((nonnull, malloc));
+
+  elfutils::dwarf open_dwarf (Dwarf *dw);
+}
+
+#endif /* _DWARFLINT_FILES_H_ */
diff --git a/dwarflint/highlevel_check.cc b/dwarflint/highlevel_check.cc
new file mode 100644 (file)
index 0000000..071ad93
--- /dev/null
@@ -0,0 +1,33 @@
+/* Initialization of high-level check context
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "highlevel_check.hh"
+#include "lowlevel_checks.hh"
+#include "files.hh"
+
+open_highlevel_dwarf::open_highlevel_dwarf (checkstack &stack, dwarflint &lint)
+  : _m_dwfl ((lint.check<lowlevel_checks> (stack),
+             files::open_dwfl ()))
+  , c_dw (files::open_dwarf (_m_dwfl, lint.fname (), lint.fd ()))
+  , dw (files::open_dwarf (c_dw))
+{}
+
+open_highlevel_dwarf::~open_highlevel_dwarf ()
+{
+  dwfl_end (_m_dwfl);
+}
diff --git a/dwarflint/highlevel_check.hh b/dwarflint/highlevel_check.hh
new file mode 100644 (file)
index 0000000..46b4d38
--- /dev/null
@@ -0,0 +1,79 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_CHECKS_HIGH_HH
+#define DWARFLINT_CHECKS_HIGH_HH
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "checks.hh"
+#include "check_debug_info.hh"
+
+#include "../libdw/c++/dwarf"
+#include "../libdwfl/libdwfl.h"
+
+class open_highlevel_dwarf
+  : public check<open_highlevel_dwarf>
+{
+  Dwfl *const _m_dwfl;
+public:
+  static checkdescriptor const *descriptor () {
+    static checkdescriptor cd
+      (checkdescriptor::create ("open_highlevel_dwarf")
+       .hidden ());
+    return &cd;
+  }
+
+  Dwarf *const c_dw;
+  elfutils::dwarf const dw;
+  open_highlevel_dwarf (checkstack &stack, dwarflint &lint);
+  ~open_highlevel_dwarf ();
+};
+
+struct highlevel_check_i
+{
+  open_highlevel_dwarf *_m_loader;
+  Dwarf *const c_dw;
+  elfutils::dwarf const &dw;
+
+  highlevel_check_i (checkstack &stack, dwarflint &lint)
+    : _m_loader (lint.check (stack, _m_loader))
+    , c_dw (_m_loader->c_dw)
+    , dw (_m_loader->dw)
+  {}
+};
+
+template<class T>
+class highlevel_check
+  : public check<highlevel_check<T> >
+  , public highlevel_check_i
+{
+  open_highlevel_dwarf *_m_loader;
+public:
+  static checkdescriptor const *descriptor () {
+    static checkdescriptor cd ("highlevel_check");
+    return &cd;
+  }
+
+  highlevel_check (checkstack &stack, dwarflint &lint)
+    : highlevel_check_i (stack, lint)
+  {}
+};
+
+#endif//DWARFLINT_CHECKS_HIGH_HH
diff --git a/dwarflint/highlevel_check_i.hh b/dwarflint/highlevel_check_i.hh
new file mode 100644 (file)
index 0000000..ddf38e6
--- /dev/null
@@ -0,0 +1 @@
+class highlevel_check_i;
diff --git a/dwarflint/locstats.cc b/dwarflint/locstats.cc
new file mode 100644 (file)
index 0000000..7602e7f
--- /dev/null
@@ -0,0 +1,657 @@
+/*
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "highlevel_check.hh"
+#include "all-dies-it.hh"
+#include "option.hh"
+#include "pri.hh"
+#include "files.hh"
+
+#include <libintl.h>
+
+#include <sstream>
+#include <bitset>
+
+using elfutils::dwarf;
+
+#define DIE_OPTSTRING                          \
+  "}[,...]"
+
+global_opt<string_option> opt_ignore
+  ("Skip certain DIEs.  class may be one of single_addr, artificial, inlined, \
+inlined_subroutine, no_coverage, mutable, or immutable.",
+   "class[,...]", "ignore");
+
+global_opt<string_option> opt_dump
+  ("Dump certain DIEs.  For classes, see option 'ignore'.",
+   "class[,...]", "dump");
+
+global_opt<string_option> opt_tabulation_rule
+  ("Rule for sorting results into buckets. start is either integer 0..100, \
+or special value 0.0 indicating cases with no coverage whatsoever \
+(i.e. not those that happen to round to 0%).",
+   "start[:step][,...]", "tabulate");
+
+// where.c needs to know how to format certain wheres.  The module
+// doesn't know that we don't use these :)
+extern "C"
+bool
+show_refs ()
+{
+  return false;
+}
+
+#define DIE_TYPES              \
+  TYPE(single_addr)            \
+  TYPE(artificial)             \
+  TYPE(inlined)                        \
+  TYPE(inlined_subroutine)     \
+  TYPE(no_coverage)            \
+  TYPE(mutable)                        \
+  TYPE(immutable)
+
+struct tabrule
+{
+  int start;
+  int step;
+  tabrule (int a_start, int a_step)
+    : start (a_start), step (a_step)
+  {}
+  bool operator < (tabrule const &other) const {
+    return start < other.start;
+  }
+};
+
+// Sharp 0.0% coverage (i.e. not a single address byte is covered)
+const int cov_00 = -1;
+
+struct tabrules_t
+  : public std::vector<tabrule>
+{
+  tabrules_t (std::string const &rule)
+  {
+    std::stringstream ss;
+    ss << rule;
+
+    std::string item;
+    while (std::getline (ss, item, ','))
+      {
+       if (item.empty ())
+         continue;
+       int start;
+       int step;
+       char const *ptr = item.c_str ();
+
+       if (item.length () >= 3
+           && std::strncmp (ptr, "0.0", 3) == 0)
+         {
+           start = cov_00;
+           ptr += 3;
+         }
+       else
+         start = std::strtol (ptr, const_cast<char **> (&ptr), 10);
+
+       if (*ptr == 0)
+         step = 0;
+       else
+         {
+           if (*ptr != ':')
+             {
+               step = 0;
+               goto garbage;
+             }
+           else
+             ptr++;
+
+           step = std::strtol (ptr, const_cast<char **> (&ptr), 10);
+           if (*ptr != 0)
+           garbage:
+             std::cerr << "Ignoring garbage at the end of the rule item: '"
+                       << ptr << '\'' << std::endl;
+         }
+
+       push_back (tabrule (start, step));
+      }
+
+    push_back (tabrule (100, 0));
+    std::sort (begin (), end ());
+  }
+
+  void next ()
+  {
+    if (at (0).step == 0)
+      erase (begin ());
+    else
+      {
+       if (at (0).start == cov_00)
+         at (0).start = 0;
+       at (0).start += at (0).step;
+       if (size () > 1)
+         {
+           if (at (0).start > at (1).start)
+             erase (begin ());
+           while (size () > 1
+                  && at (0).start == at (1).start)
+             erase (begin ());
+         }
+      }
+  }
+
+  bool match (int value) const
+  {
+    return at (0).start == value;
+  }
+};
+
+#define TYPE(T) dt_##T,
+  enum die_type_e
+    {
+      DIE_TYPES
+      dt__count
+    };
+#undef TYPE
+
+class die_type_matcher
+  : public std::bitset<dt__count>
+{
+  class invalid {};
+  std::pair<die_type_e, bool>
+  parse (std::string &desc)
+  {
+    bool val = true;
+    if (desc == "")
+      throw invalid ();
+
+#define TYPE(T)                                        \
+    if (desc == #T)                            \
+      return std::make_pair (dt_##T, val);
+    DIE_TYPES
+#undef TYPE
+
+      throw invalid ();
+  }
+
+public:
+  die_type_matcher (std::string const &rule)
+  {
+    std::stringstream ss;
+    ss << rule;
+
+    std::string item;
+    while (std::getline (ss, item, ','))
+      try
+       {
+         std::pair<die_type_e, bool> const &ig = parse (item);
+         set (ig.first, ig.second);
+       }
+      catch (invalid &i)
+       {
+         std::cerr << "Invalid die type: " << item << std::endl;
+       }
+  }
+};
+
+class mutability_t
+{
+  bool _m_is_mutable;
+  bool _m_is_immutable;
+
+public:
+  mutability_t ()
+    : _m_is_mutable (false)
+    , _m_is_immutable (false)
+  {
+  }
+
+  void set (bool what)
+  {
+    if (what)
+      _m_is_mutable = true;
+    else
+      _m_is_immutable = true;
+  }
+
+  void set_both ()
+  {
+    set (true);
+    set (false);
+  }
+
+  void locexpr (Dwarf_Op *expr, size_t len)
+  {
+    // We scan the expression looking for DW_OP_{bit_,}piece
+    // operators which mark ends of sub-expressions to us.
+    bool m = false;
+    for (size_t i = 0; i < len; ++i)
+      switch (expr[i].atom)
+       {
+       case DW_OP_implicit_value:
+       case DW_OP_stack_value:
+         m = true;
+         break;
+
+       case DW_OP_bit_piece:
+       case DW_OP_piece:
+         set (m);
+         m = false;
+         break;
+       };
+    set (m);
+  }
+
+  bool is_mutable () const { return _m_is_mutable; }
+  bool is_immutable () const { return _m_is_immutable; }
+};
+
+struct error
+  : public std::runtime_error
+{
+  explicit error (std::string const &what_arg)
+    : std::runtime_error (what_arg)
+  {}
+};
+
+// Look through the stack of parental dies and return the non-empty
+// ranges instance closest to the stack top (i.e. die_stack.end ()).
+dwarf::ranges
+find_ranges (std::vector<dwarf::debug_info_entry> const &die_stack)
+{
+  for (auto it = die_stack.rbegin (); it != die_stack.rend (); ++it)
+    if (!it->ranges ().empty ())
+      return it->ranges ();
+  throw error ("no ranges for this DIE");
+}
+
+bool
+is_inlined (dwarf::debug_info_entry const &die)
+{
+  dwarf::debug_info_entry::attributes_type::const_iterator it
+    = die.attributes ().find (DW_AT_inline);
+  if (it != die.attributes ().end ())
+    {
+      char const *name = (*it).second.dwarf_constant ().name ();
+      return std::strcmp (name, "declared_inlined") == 0
+       || std::strcmp (name, "inlined") == 0;
+    }
+  return false;
+}
+
+void
+process(Dwarf *c_dw, dwarf const &dw)
+{
+  // map percentage->occurrences.  Percentage is cov_00..100, where
+  // 0..100 is rounded-down integer division.
+  std::map<int, unsigned long> tally;
+  unsigned long total = 0;
+  for (int i = 0; i <= 100; ++i)
+    tally[i] = 0;
+
+  tabrules_t tabrules (opt_tabulation_rule.seen ()
+                      ? opt_tabulation_rule.value () : "10:10");
+  die_type_matcher ignore (opt_ignore.seen () ? opt_ignore.value () : "");
+  die_type_matcher dump (opt_dump.seen () ? opt_dump.value () : "");
+  std::bitset<dt__count> interested = ignore | dump;
+  bool interested_mutability
+    = interested.test (dt_mutable) || interested.test (dt_immutable);
+
+  for (all_dies_iterator<dwarf> it = all_dies_iterator<dwarf> (dw);
+       it != all_dies_iterator<dwarf> (); ++it)
+    {
+      std::bitset<dt__count> die_type;
+      dwarf::debug_info_entry const &die = *it;
+
+      // We are interested in variables and formal parameters
+      bool is_formal_parameter = die.tag () == DW_TAG_formal_parameter;
+      if (!is_formal_parameter && die.tag () != DW_TAG_variable)
+       continue;
+
+      dwarf::debug_info_entry::attributes_type const &attrs
+       = die.attributes ();
+
+      // ... except those that are just declarations
+      if (attrs.find (DW_AT_declaration) != attrs.end ())
+       continue;
+
+      if (ignore.test (dt_artificial)
+         && attrs.find (DW_AT_artificial) != attrs.end ())
+       continue;
+
+      // Of formal parameters we ignore those that are children of
+      // subprograms that are themselves declarations.
+      std::vector<dwarf::debug_info_entry> const &die_stack = it.stack ();
+      dwarf::debug_info_entry const &parent = *(die_stack.rbegin () + 1);
+      if (is_formal_parameter)
+       if (parent.tag () == DW_TAG_subroutine_type
+           || (parent.attributes ().find (DW_AT_declaration)
+               != parent.attributes ().end ()))
+         continue;
+
+      if (interested.test (dt_inlined)
+         || interested.test (dt_inlined_subroutine))
+       {
+         bool inlined = false;
+         bool inlined_subroutine = false;
+         for (std::vector<dwarf::debug_info_entry>::const_reverse_iterator
+                stit = die_stack.rbegin (); stit != die_stack.rend (); ++stit)
+           {
+             if (interested.test (dt_inlined)
+                 && stit->tag () == DW_TAG_subprogram
+                 && is_inlined (*stit))
+               {
+                 inlined = true;
+                 if (interested.test (dt_inlined_subroutine)
+                     && inlined_subroutine)
+                   break;
+               }
+             if (interested.test (dt_inlined_subroutine)
+                 && stit->tag () == DW_TAG_inlined_subroutine)
+               {
+                 inlined_subroutine = true;
+                 if (interested.test (dt_inlined)
+                     && inlined)
+                   break;
+               }
+           }
+
+         if (inlined)
+           {
+             if (ignore.test (dt_inlined))
+               continue;
+             die_type.set (dt_inlined);
+           }
+         if (inlined_subroutine)
+           {
+             if (ignore.test (dt_inlined_subroutine))
+               continue;
+             die_type.set (dt_inlined_subroutine, inlined_subroutine);
+           }
+       }
+
+      // Unfortunately the location expression is not yet wrapped
+      // in c++, so we need to revert back to C code.
+      Dwarf_Die die_c_mem,
+       *die_c = dwarf_offdie (c_dw, die.offset (), &die_c_mem);
+      assert (die_c != NULL);
+
+      Dwarf_Attribute locattr_mem,
+       *locattr = dwarf_attr_integrate (die_c, DW_AT_location, &locattr_mem);
+
+      // Also ignore extern globals -- these have DW_AT_external and
+      // no DW_AT_location.
+      if (attrs.find (DW_AT_external) != attrs.end () && locattr == NULL)
+       continue;
+
+      /*
+      Dwarf_Attribute name_attr_mem,
+       *name_attr = dwarf_attr_integrate (die_c, DW_AT_name, &name_attr_mem);
+      std::string name = name_attr != NULL
+       ? dwarf_formstring (name_attr)
+       : (dwarf_hasattr_integrate (die_c, DW_AT_artificial)
+          ? "<artificial>" : "???");
+
+      std::cerr << "die=" << std::hex << die.offset ()
+               << " '" << name << '\'';
+      */
+
+      int coverage;
+      Dwarf_Op *expr;
+      size_t len;
+      mutability_t mut;
+
+      // consts need no location
+      if (attrs.find (DW_AT_const_value) != attrs.end ())
+       {
+         coverage = 100;
+         if (interested_mutability)
+           mut.set (true);
+       }
+
+      // no location
+      else if (locattr == NULL)
+       {
+         coverage = cov_00;
+         if (interested_mutability)
+           mut.set_both ();
+       }
+
+      // non-list location
+      else if (dwarf_getlocation (locattr, &expr, &len) == 0)
+       {
+         // Globals and statics have non-list location that is a
+         // singleton DW_OP_addr expression.
+         if (len == 1 && expr[0].atom == DW_OP_addr)
+           {
+             if (ignore.test (dt_single_addr))
+               continue;
+             die_type.set (dt_single_addr);
+           }
+         if (interested_mutability)
+           mut.locexpr (expr, len);
+         coverage = (len == 0) ? cov_00 : 100;
+       }
+
+      // location list
+      else
+       {
+         try
+           {
+             dwarf::ranges ranges (find_ranges (die_stack));
+             size_t length = 0;
+             size_t covered = 0;
+
+             // Arbitrarily assume that there will be no more than 10
+             // expressions per address.
+             size_t nlocs = 10;
+             Dwarf_Op *exprs[nlocs];
+             size_t exprlens[nlocs];
+
+             for (dwarf::ranges::const_iterator rit = ranges.begin ();
+                  rit != ranges.end (); ++rit)
+               {
+                 Dwarf_Addr low = (*rit).first;
+                 Dwarf_Addr high = (*rit).second;
+                 length += high - low;
+                 //std::cerr << " " << low << ".." << high << std::endl;
+
+                 for (Dwarf_Addr addr = low; addr < high; ++addr)
+                   {
+                     int got = dwarf_getlocation_addr (locattr, addr,
+                                                       exprs, exprlens, nlocs);
+                     if (got < 0)
+                       throw ::error (std::string ("dwarf_getlocation_addr: ")
+                                      + dwarf_errmsg (-1));
+
+                     // At least one expression for the address must
+                     // be of non-zero length for us to count that
+                     // address as covered.
+                     for (int i = 0; i < got; ++i)
+                       {
+                         if (interested_mutability)
+                           mut.locexpr (exprs[i], exprlens[i]);
+                         if (exprlens[i] > 0)
+                           {
+                             covered++;
+                             break;
+                           }
+                       }
+                   }
+               }
+
+             if (length == 0)
+               throw ::error ("zero-length range");
+
+             if (covered == 0)
+               coverage = cov_00;
+             else
+               coverage = 100 * covered / length;
+           }
+         catch (::error const &e)
+           {
+             std::cerr << "error: " << die_locus (die) << ": "
+                       << e.what () << '.' << std::endl;
+             continue;
+           }
+       }
+
+      if (coverage == cov_00)
+       {
+         if (ignore.test (dt_no_coverage))
+           continue;
+         die_type.set (dt_no_coverage);
+       }
+      else if (interested_mutability)
+       {
+         assert (mut.is_mutable () || mut.is_immutable ());
+         if (mut.is_mutable ())
+           {
+             if (ignore.test (dt_mutable))
+               continue;
+             die_type.set (dt_mutable);
+           }
+         if (mut.is_immutable ())
+           {
+             if (ignore.test (dt_immutable))
+               continue;
+             die_type.set (dt_immutable);
+           }
+       }
+
+      if ((dump & die_type).any ())
+       {
+#define TYPE(T) << (die_type.test (dt_##T) ? " "#T : "")
+         std::cerr << "dumping" DIE_TYPES << " DIE" << std::endl;
+#undef TYPE
+
+         std::string pad = "";
+         for (auto sit = die_stack.begin (); sit != die_stack.end (); ++sit)
+           {
+             auto const &d = *sit;
+             std::cerr << pad << pri::ref (d) << " "
+                       << elfutils::dwarf::tags::name (d.tag ()) << std::endl;
+             for (auto atit = d.attributes ().begin ();
+                  atit != d.attributes ().end (); ++atit)
+               {
+                 auto const &attr = *atit;
+                 std::cerr << pad << "    " << to_string (attr) << std::endl;
+               }
+             pad += " ";
+           }
+
+         std::cerr << "empty coverage " << pri::ref (die) << " "
+                   << to_string (die) << std::endl;
+       }
+
+      tally[coverage]++;
+      total++;
+      //std::cerr << std::endl;
+    }
+
+  unsigned long cumulative = 0;
+  unsigned long last = 0;
+  int last_pct = cov_00;
+  if (total == 0)
+    {
+      std::cout << "No coverage recorded." << std::endl;
+      return;
+    }
+
+  std::cout << "cov%\tsamples\tcumul" << std::endl;
+  for (int i = cov_00; i <= 100; ++i)
+    {
+      cumulative += tally.find (i)->second;
+      if (tabrules.match (i))
+       {
+         long int samples = cumulative - last;
+
+         // The case 0.0..x should be printed simply as 0
+         if (last_pct == cov_00 && i > cov_00)
+           last_pct = 0;
+
+         if (last_pct == cov_00)
+           std::cout << "0.0";
+         else
+           std::cout << std::dec << last_pct;
+
+         if (last_pct != i)
+           std::cout << ".." << i;
+         std::cout << "\t" << samples
+                   << '/' << (100*samples / total) << '%'
+                   << "\t" << cumulative
+                   << '/' << (100*cumulative / total) << '%'
+                   << std::endl;
+         last = cumulative;
+         last_pct = i + 1;
+
+         tabrules.next ();
+       }
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+  /* Set locale.  */
+  setlocale (LC_ALL, "");
+
+  /* Initialize the message catalog.  */
+  textdomain (PACKAGE_TARNAME);
+
+  /* Parse and process arguments.  */
+  argppp argp (global_opts ());
+  int remaining;
+  argp.parse (argc, argv, 0, &remaining);
+
+  if (remaining == argc)
+    {
+      fputs (gettext ("Missing file name.\n"), stderr);
+      argp.help (stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+                program_invocation_short_name);
+      std::exit (1);
+    }
+
+  bool only_one = remaining + 1 == argc;
+  do
+    {
+      try
+       {
+         char const *fname = argv[remaining];
+         if (!only_one)
+           std::cout << std::endl << fname << ":" << std::endl;
+
+         int fd = files::open (fname);
+         Dwfl *dwfl = files::open_dwfl ();
+         Dwarf *c_dw = files::open_dwarf (dwfl, fname, fd);
+         dwarf dw = files::open_dwarf (c_dw);
+
+         process (c_dw, dw);
+
+         close (fd);
+         dwfl_end (dwfl);
+       }
+      catch (std::runtime_error &e)
+       {
+         std::cerr << "error: "
+                   << e.what () << '.' << std::endl;
+         continue;
+       }
+    }
+  while (++remaining < argc);
+}
diff --git a/dwarflint/locus.cc b/dwarflint/locus.cc
new file mode 100644 (file)
index 0000000..105ef41
--- /dev/null
@@ -0,0 +1,64 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2008,2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "locus.hh"
+#include "section_id.hh"
+#include <sstream>
+#include <iostream>
+
+std::ostream &
+operator << (std::ostream &os, locus const &loc)
+{
+  os << loc.format ();
+  return os;
+}
+
+char const *
+locus_simple_fmt::offset_n ()
+{
+  return "offset";
+}
+
+void
+locus_simple_fmt::hex (std::ostream &ss, uint64_t off)
+{
+  ss << "0x" << std::hex << off;
+}
+
+void
+locus_simple_fmt::dec (std::ostream &ss, uint64_t off)
+{
+  ss << std::dec << off;
+}
+
+std::string
+simple_locus_aux::format_simple_locus (char const *(*N) (),
+                                      void (*F) (std::ostream &, uint64_t),
+                                      bool brief, section_id sec, uint64_t off)
+{
+  std::stringstream ss;
+  if (!brief)
+    ss << section_name[sec];
+  if (off != (uint64_t)-1)
+    {
+      if (!brief)
+       ss << ": ";
+      ss << N() << " ";
+      F (ss, off);
+    }
+  return ss.str ();
+}
diff --git a/dwarflint/locus.hh b/dwarflint/locus.hh
new file mode 100644 (file)
index 0000000..f594f44
--- /dev/null
@@ -0,0 +1,104 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_WHERE_HH
+#define DWARFLINT_WHERE_HH
+
+#include "section_id.hh"
+
+#include <stdint.h>
+#include <iosfwd>
+#include <string>
+
+/// Instances of the locus subclasses are used as pointers into
+/// debuginfo for documentation purposes (messages and errors).  They
+/// are usually tiny structures, and should be used as values, but we
+/// need the abstract interface to be able to format them, and copy
+/// them into ref_record.
+class locus
+{
+public:
+  virtual std::string format (bool brief = false) const = 0;
+  virtual ~locus () {}
+};
+
+std::ostream &operator << (std::ostream &os, locus const &loc);
+
+/// Helper class for simple_locus to reduce the template bloat.
+class simple_locus_aux
+{
+protected:
+  static std::string format_simple_locus (char const *(*N) (),
+                                         void (*F) (std::ostream &, uint64_t),
+                                         bool brief, section_id sec,
+                                         uint64_t off);
+};
+
+/// Template for quick construction of straightforward locus
+/// subclasses (one address, one way of formatting).  N should be a
+/// function that returns the name of the argument.
+/// locus_simple_fmt::hex, locus_simple_fmt::dec would be candidate
+/// parameters for argument F.
+template<char const *(*N) (),
+        void (*F) (std::ostream &, uint64_t)>
+class simple_locus
+  : public locus
+  , private simple_locus_aux
+{
+  section_id _m_sec;
+  uint64_t _m_offset;
+
+public:
+  explicit simple_locus (section_id sec, uint64_t offset = -1)
+    : _m_sec (sec)
+    , _m_offset (offset)
+  {}
+
+  std::string
+  format (bool brief = false) const
+  {
+    return format_simple_locus (N, F, brief, _m_sec, _m_offset);
+  }
+};
+
+/// Constructor of simple_locus that fixes the section_id argument.
+template<section_id S,
+        char const *(*N) (),
+        void (*F) (std::ostream &, uint64_t)>
+class fixed_locus
+  : public simple_locus<N, F>
+{
+public:
+  explicit fixed_locus (uint64_t offset = -1)
+    : simple_locus<N, F> (S, offset)
+  {}
+};
+
+namespace locus_simple_fmt
+{
+  char const *offset_n ();
+  void hex (std::ostream &ss, uint64_t off);
+  void dec (std::ostream &ss, uint64_t off);
+}
+
+/// Straightforward locus for cases where either offset is not
+/// necessary at all, or if it is present, it's simply shown as
+/// "offset: 0xf00".
+typedef simple_locus<locus_simple_fmt::offset_n,
+                    locus_simple_fmt::hex> section_locus;
+
+#endif//DWARFLINT_WHERE_HH
diff --git a/dwarflint/lowlevel_checks.cc b/dwarflint/lowlevel_checks.cc
new file mode 100644 (file)
index 0000000..13b92d9
--- /dev/null
@@ -0,0 +1,87 @@
+/* Scheduler for low_level checks
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "lowlevel_checks.hh"
+#include "sections.hh"
+#include "check_debug_info.hh"
+#include "check_debug_abbrev.hh"
+#include "check_debug_aranges.hh"
+#include "check_debug_pub.hh"
+#include "check_debug_loc_range.hh"
+#include "check_debug_line.hh"
+
+checkdescriptor const *
+lowlevel_checks::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("lowlevel_checks")
+     .hidden ());
+  return &cd;
+}
+
+static reg<lowlevel_checks> reg_lowlevel_checks;
+
+namespace
+{
+  template<class T> struct do_check {
+    static bool check (checkstack &stack, dwarflint &lint) {
+      return lint.toplev_check<T> (stack);
+    }
+  };
+
+  // There is no separate check_debug_str pass.  Make a stub so that
+  // we can do it all in one macro-expanded sweep below.
+#define STUBBED_CHECK(NAME)                                            \
+  struct check_debug_##NAME {};                                                \
+  template<> struct do_check<check_debug_##NAME> {                     \
+    static bool check (__attribute__ ((unused)) checkstack &stack,     \
+                      __attribute__ ((unused)) dwarflint &lint)        \
+    {                                                                  \
+      return true;                                                     \
+    }                                                                  \
+  }
+  STUBBED_CHECK(str);
+  STUBBED_CHECK(mac);
+#undef STUBBED_CHECK
+}
+
+lowlevel_checks::lowlevel_checks (checkstack &stack, dwarflint &lint)
+{
+  // Then check all the debug sections that are there.  For each
+  // existing section request that the check passes.  Re-requesting
+  // already-passed checks is OK, the scheduler caches it.
+  bool passed = true;
+
+#define SEC(NAME)                                                      \
+  section<sec_##NAME> *NAME =                                          \
+    lint.toplev_check<section<sec_##NAME> > (stack);                   \
+  if (NAME != NULL)                                                    \
+    if (!do_check<check_debug_##NAME>::check (stack, lint))            \
+      passed = false;
+
+  DEBUGINFO_SECTIONS;
+#undef SEC
+
+  lint.check<check_debug_info_refs> (stack);
+
+  if (!passed)
+    throw check_base::failed ();
+}
diff --git a/dwarflint/lowlevel_checks.hh b/dwarflint/lowlevel_checks.hh
new file mode 100644 (file)
index 0000000..518bb85
--- /dev/null
@@ -0,0 +1,32 @@
+/* Scheduler for low_level checks
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_LOWLEVEL_CHECKS_HH
+#define DWARFLINT_LOWLEVEL_CHECKS_HH
+
+#include "checks.hh"
+
+class lowlevel_checks
+  : public check<lowlevel_checks>
+{
+public:
+  static checkdescriptor const *descriptor ();
+  lowlevel_checks (checkstack &stack, dwarflint &lint);
+};
+
+#endif//DWARFLINT_LOWLEVEL_CHECKS_HH
diff --git a/dwarflint/main.cc b/dwarflint/main.cc
new file mode 100644 (file)
index 0000000..dc4abda
--- /dev/null
@@ -0,0 +1,224 @@
+/* Main entry point for dwarflint, a pedantic checker for DWARF files.
+   Copyright (C) 2008,2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <libintl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <iostream>
+#include <sstream>
+
+#include "dwarflint.hh"
+#include "readctx.hh"
+#include "checks.hh"
+#include "option.hh"
+#include "messages.hh"
+
+/* Messages that are accepted (and made into warning).  */
+struct message_criteria warning_criteria;
+
+/* Accepted (warning) messages, that are turned into errors.  */
+struct message_criteria error_criteria;
+
+struct check_option_t
+  : public global_opt<option_common>
+{
+  struct initial_checkrules
+    : public checkrules
+  {
+    initial_checkrules ()
+    {
+      push_back (checkrule_internal ("@all", checkrule::request));
+      push_back (checkrule_internal ("@nodefault", checkrule::forbid));
+    }
+  } rules;
+
+  check_option_t ()
+    : global_opt<option_common> ("Only run selected checks.",
+                                "[+-][@]name,...", "check", 0)
+  {}
+
+  error_t parse_opt (char *arg, __attribute__ ((unused)) argp_state *state)
+  {
+    static bool first = true;
+    std::stringstream ss (arg);
+    std::string item;
+
+    while (std::getline (ss, item, ','))
+      {
+       if (item.empty ())
+         continue;
+
+       enum
+       {
+         forbid,
+         request,
+         replace
+       } act;
+
+       // If the first rule has no operator, we assume the user
+       // wants to replace the implicit set of checks.
+       if (first)
+         {
+           act = replace;
+           first = false;
+         }
+       else
+         // Otherwise the rules are implicitly requesting, even
+         // without the '+' operator.
+         act = request;
+
+       bool minus = item[0] == '-';
+       bool plus = item[0] == '+';
+       if (plus || minus)
+         item = item.substr (1);
+       if (plus)
+         act = request;
+       if (minus)
+         act = forbid;
+
+       if (act == replace)
+         {
+           rules.clear ();
+           act = request;
+         }
+
+       checkrule::action_t action
+         = act == request ? checkrule::request : checkrule::forbid;
+       rules.push_back (checkrule (item, action));
+      }
+    return 0;
+  }
+} check_option;
+
+global_opt<void_option>
+  be_quiet ("Do not print anything if successful",
+           "quiet", 'q');
+
+global_opt<string_option>
+  opt_list_checks ("List all the available checks.",
+                  "full", "list-checks", 0,
+                  OPTION_ARG_OPTIONAL);
+
+// xxx The following three should go away when we introduce the
+// message filtering.  Or should be preserved, but in a way that makes
+// more sense, right now they are simply a misnomer.
+global_opt<void_option>
+  ignore_bloat ("Ignore messages related to bloat.", "ignore-bloat");
+global_opt<void_option>
+  be_strict ("Be somewhat stricter.", "strict");
+global_opt<void_option>
+  be_tolerant ("Be somewhat more tolerant.", "tolerant");
+
+int
+main (int argc, char *argv[])
+{
+  /* Set locale.  */
+  setlocale (LC_ALL, "");
+
+  /* Initialize the message catalog.  */
+  textdomain (PACKAGE_TARNAME);
+
+  /* Parse and process arguments.  */
+  argppp argp (global_opts (),
+              dwarflint::main_registrar ()->get_descriptors ());
+
+  int remaining;
+  argp.parse (argc, argv, 0, &remaining);
+
+  if (opt_list_checks.seen ())
+    {
+      dwarflint::list_checks ();
+      std::exit (0);
+    }
+  else if (remaining == argc)
+    {
+      fputs (gettext ("Missing file name.\n"), stderr);
+      argp.help (stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+                program_invocation_short_name);
+      std::exit (1);
+    }
+
+  /* Initialize warning & error criteria.  */
+  warning_criteria |= message_term (mc_none, mc_none);
+
+  error_criteria |= message_term (mc_impact_4, mc_none);
+  error_criteria |= message_term (mc_error, mc_none);
+
+  /* Configure warning & error criteria according to configuration.  */
+  if (ignore_bloat)
+    warning_criteria &= message_term (mc_none, mc_acc_bloat);
+
+  if (!be_strict)
+    {
+      warning_criteria &= message_term (mc_none, mc_strings);
+      warning_criteria.and_not (mc_line | mc_acc_bloat);
+      warning_criteria &= message_term (mc_none, mc_pubtypes);
+    }
+
+  if (be_tolerant)
+    {
+      warning_criteria &= message_term (mc_none, mc_loc);
+      warning_criteria &= message_term (mc_none, mc_ranges);
+    }
+
+  if (false) // for debugging
+    {
+      std::cout << "warning criteria: " << warning_criteria << std::endl;
+      std::cout << "error criteria:   " << error_criteria << std::endl;
+    }
+
+  /* Before we start tell the ELF library which version we are using.  */
+  elf_version (EV_CURRENT);
+
+  /* Now process all the files given at the command line.  */
+  bool only_one = remaining + 1 == argc;
+  bool one_passed = false;
+  do
+    {
+      try
+       {
+         char const *fname = argv[remaining];
+         if (!only_one)
+           std::cout << std::endl << fname << ":" << std::endl;
+         wr_reset_counters ();
+         dwarflint lint (fname, check_option.rules);
+         one_passed = true;
+
+         if (error_count == 0 && !be_quiet)
+           puts (gettext ("No errors"));
+       }
+      catch (std::runtime_error &e)
+       {
+         wr_error () << e.what () << std::endl;
+         continue;
+       }
+    }
+  while (++remaining < argc);
+
+  if (one_passed)
+    for (checkrules::const_iterator it = check_option.rules.begin ();
+        it != check_option.rules.end (); ++it)
+      if (!it->used ())
+       std::cerr << "warning: the rule `" << it->name ()
+                 << "' never matched." << std::endl;
+
+  return error_count != 0;
+}
diff --git a/dwarflint/main.hh b/dwarflint/main.hh
new file mode 100644 (file)
index 0000000..96368f5
--- /dev/null
@@ -0,0 +1,25 @@
+/* Pedantic checker for DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_MAIN_HH
+#define DWARFLINT_MAIN_HH
+
+#include "option.hh"
+extern string_option opt_list_checks;
+
+#endif//DWARFLINT_MAIN_HH
diff --git a/dwarflint/messages.cc b/dwarflint/messages.cc
new file mode 100644 (file)
index 0000000..6b6cade
--- /dev/null
@@ -0,0 +1,491 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009-2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "messages.hh"
+#include "coverage.hh"
+#include "option.hh"
+
+#include <vector>
+#include <sstream>
+#include <cassert>
+#include <cstdarg>
+#include <libintl.h>
+
+unsigned error_count = 0;
+bool message_context::_m_last_emitted;
+
+bool
+message_accept (struct message_criteria const *cri,
+               unsigned long cat)
+{
+  for (size_t i = 0; i < cri->size (); ++i)
+    {
+      message_term const &t = cri->at (i);
+      if ((t.positive & cat) == t.positive
+         && (t.negative & cat) == 0)
+       return true;
+    }
+
+  return false;
+}
+
+namespace
+{
+  struct cat_to_str
+    : public std::vector<std::string>
+  {
+    cat_to_str ()
+    {
+      int count = 0;
+#define MC(CAT, ID) if (ID > count) count = ID;
+      MESSAGE_CATEGORIES
+#undef MC
+
+      resize (count + 1);
+#define MC(CAT, ID) (*this)[ID] = #CAT;
+      MESSAGE_CATEGORIES
+#undef MC
+    }
+  } cat_names;
+  size_t cat_max = cat_names.size ();
+}
+
+
+message_category
+operator | (message_category a, message_category b)
+{
+  return static_cast<message_category> ((unsigned long)a | b);
+}
+
+message_category &
+operator |= (message_category &a, message_category b)
+{
+  a = a | b;
+  return a;
+}
+
+std::string
+message_term::str () const
+{
+  std::ostringstream os;
+  os << '(';
+
+  bool got = false;
+  for (size_t i = 0; i <= cat_max; ++i)
+    {
+      size_t mask = 1u << i;
+      if ((positive & mask) != 0
+         || (negative & mask) != 0)
+       {
+         if (got)
+           os << " & ";
+         if ((negative & (1u << i)) != 0)
+           os << '~';
+         os << cat_names[i];
+         got = true;
+       }
+    }
+
+  if (!got)
+    os << '1';
+
+  os << ')';
+  return os.str ();
+}
+
+std::string
+message_criteria::str () const
+{
+  std::ostringstream os;
+
+  for (size_t i = 0; i < size (); ++i)
+    {
+      message_term const &t = at (i);
+      if (i > 0)
+       os << " | ";
+      os << t.str ();
+    }
+
+  return os.str ();
+}
+
+void
+message_criteria::operator &= (message_term const &term)
+{
+  assert ((term.positive & term.negative) == 0);
+  for (size_t i = 0; i < size (); )
+    {
+      message_term &t = at (i);
+      t.positive = t.positive | term.positive;
+      t.negative = t.negative | term.negative;
+      if ((t.positive & t.negative) != 0)
+       /* A ^ ~A -> drop the term.  */
+       erase (begin () + i);
+      else
+       ++i;
+    }
+}
+
+void
+message_criteria::operator |= (message_term const &term)
+{
+  assert ((term.positive & term.negative) == 0);
+  push_back (term);
+}
+
+// xxx this one is inaccessible from the outside.  Make it like &=, |=
+// above
+/* NEG(a&b&~c) -> (~a + ~b + c) */
+message_criteria
+operator ! (message_term const &term)
+{
+  assert ((term.positive & term.negative) == 0);
+
+  message_criteria ret;
+  for (size_t i = 0; i < cat_max; ++i)
+    {
+      unsigned mask = 1u << i;
+      if ((term.positive & mask) != 0)
+       ret |= message_term ((message_category)(1u << i), mc_none);
+      else if ((term.negative & mask) != 0)
+       ret |= message_term (mc_none, (message_category)(1u << i));
+    }
+
+  return ret;
+}
+
+std::ostream &
+operator<< (std::ostream &o, message_category cat)
+{
+  o << '(';
+
+  bool got = false;
+  for (size_t i = 0; i <= cat_max; ++i)
+    {
+      size_t mask = 1u << i;
+      if ((cat & mask) != 0)
+       {
+         if (got)
+           o << ",";
+         o << cat_names[i];
+         got = true;
+       }
+    }
+
+  if (!got)
+    o << "none";
+
+  return o << ')';
+}
+
+std::ostream &
+operator<< (std::ostream &o, message_term const &term)
+{
+  return o << term.str ();
+}
+
+std::ostream &
+operator<< (std::ostream &o, __attribute__ ((unused)) message_criteria const &criteria)
+{
+  return o << criteria.str ();
+}
+
+/* MUL((a&b + c&d), (e&f + g&h)) -> (a&b&e&f + a&b&g&h + c&d&e&f + c&d&g&h) */
+void
+message_criteria::operator *= (message_criteria const &rhs)
+{
+  message_criteria ret;
+  for (size_t i = 0; i < size (); ++i)
+    for (size_t j = 0; j < rhs.size (); ++j)
+      {
+       message_term t1 = at (i);
+       message_term const &t2 = rhs.at (j);
+       t1.positive |= t2.positive;
+       t1.negative |= t2.negative;
+       if (t1.positive & t1.negative)
+         /* A ^ ~A -> drop the term.  */
+         continue;
+       ret |= t1;
+      }
+
+  *this = ret;
+}
+
+// xxx this one is inaccessible from the outside.  Bind it properly
+/* Reject message if TERM passes.  */
+void
+message_criteria::and_not (message_term const &term)
+{
+  // xxxxx really??  "!"??
+  message_criteria tmp = !message_term (term.negative, term.positive);
+  *this *= tmp;
+}
+
+static void
+wr_verror (locus const &loc, const char *format, va_list ap)
+{
+  printf ("error: %s", loc.format ().c_str ());
+  vprintf (format, ap);
+  ++error_count;
+}
+
+static void
+wr_vwarning (locus const &loc, const char *format, va_list ap)
+{
+  printf ("%s", loc.format ().c_str ());
+  vprintf (format, ap);
+  ++error_count;
+}
+
+void
+wr_error (locus const *loc, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  wr_verror (*loc, format, ap);
+  va_end (ap);
+}
+
+void
+wr_message (unsigned long category, locus const *loc,
+           const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  // Clumsy duplicate filtering. Use format as key.
+  bool whether = false;
+  message_category cat = (message_category) category;
+  wr_message (cat).id (format, whether);
+  if (whether && message_accept (&warning_criteria, category))
+    {
+      if (message_accept (&error_criteria, category))
+       wr_verror (*loc, format, ap);
+      else
+       wr_vwarning (*loc, format, ap);
+    }
+  va_end (ap);
+}
+
+namespace
+{
+  class nostream: public std::ostream {};
+  static nostream nostream;
+
+  std::ostream &
+  get_stream ()
+  {
+    return std::cout;
+  }
+}
+
+global_opt<unsigned_option>
+  dup_threshold_opt ("Threshold for duplicate messages."
+                    " Defaults to 16."
+                    " Use zero for no limit.",
+                    "count", "dups");
+
+namespace
+{
+  unsigned
+  dup_threshold ()
+  {
+    static unsigned t = dup_threshold_opt.value (16);
+    if (t == 0)
+      t = -1;
+    return t;
+  }
+}
+
+int
+message_count_filter::should_emit (void const *key)
+{
+  unsigned count = ++_m_counters[key];
+  if (count > dup_threshold ())
+    return 0;
+  else if (count == dup_threshold ())
+    return -1;
+  else
+    return 1;
+}
+
+message_context::message_context (message_count_filter *filter,
+                                 locus const *loc, char const *prefix)
+  : _m_filter (filter)
+  , _m_loc (loc)
+  , _m_prefix (prefix)
+{}
+
+std::ostream &
+message_context::when (bool whether) const
+{
+  _m_last_emitted = false;
+  if (whether)
+    {
+      ++error_count;
+      _m_last_emitted = true;
+
+      std::ostream &ret = get_stream ();
+      ret << _m_prefix;
+      if (_m_loc != NULL)
+       ret << _m_loc->format () << ": ";
+      return ret;
+    }
+  else
+    return nostream;
+}
+
+std::ostream &
+message_context::when_prev () const
+{
+  return when (wr_prev_emitted ());
+}
+
+std::ostream &
+message_context::id (void const *key, bool &whether)
+{
+  if (_m_filter == NULL)
+    return nostream;
+  else if (int status = _m_filter->should_emit (key))
+    {
+      if (status == -1)
+       get_stream () << "(threshold [--dups=" << dup_threshold ()
+                     << "] reached for the following message)"
+                     << std::endl;
+      whether = true;
+      return when (true);
+    }
+  else
+    return nostream;
+}
+
+std::ostream &
+message_context::id (void const *key)
+{
+  bool whether;
+  return id (key, whether);
+}
+
+std::ostream &
+message_context::operator << (char const *message)
+{
+  return id (message) << message;
+}
+
+std::ostream &
+message_context::operator << (std::string const &message)
+{
+  return *this << message.c_str ();
+}
+
+std::ostream &
+wr_error ()
+{
+  ++error_count;
+  return get_stream () << gettext ("error: ");
+}
+
+std::ostream &
+wr_error (locus const &loc)
+{
+  std::string fmt = loc.format ();
+  return wr_error () << fmt << ": ";
+}
+
+message_context
+message_context::filter_message (locus const *loc, message_category category)
+{
+  if (!message_accept (&warning_criteria, category))
+    return message_context (NULL, NULL, NULL);
+  else if (message_accept (&error_criteria, category))
+    return message_context (message_count_filter::inst (),
+                           loc, "error: ");
+  else
+    return message_context (message_count_filter::inst (),
+                           loc, "warning: ");
+}
+
+message_context
+wr_message (message_category category)
+{
+  return message_context::filter_message (NULL, category);
+}
+
+message_context
+wr_message (locus const &loc, message_category category)
+{
+  return message_context::filter_message (&loc, category);
+}
+
+void
+wr_format_padding_message (message_category category,
+                          locus const &loc,
+                          uint64_t start, uint64_t end, char const *kind)
+{
+  char msg[128];
+  wr_message (loc, category)
+    << range_fmt (msg, sizeof msg, start, end)
+    << ": " << kind << "." << std::endl;
+}
+
+void
+wr_format_leb128_message (locus const &loc,
+                         const char *what,
+                         const char *purpose,
+                         const unsigned char *begin, const unsigned char *end)
+{
+  message_category category = mc_leb128 | mc_acc_bloat | mc_impact_3;
+  char buf[(end - begin) * 3 + 1]; // 2 hexa digits+" " per byte, and term. 0
+  char *ptr = buf;
+  for (; begin < end; ++begin)
+    ptr += sprintf (ptr, " %02x", *begin);
+  wr_message (loc, category)
+    << what << ": value " << purpose << " encoded as `"
+    << (buf + 1) << "'." << std::endl;
+}
+
+void
+wr_message_padding_0 (message_category category,
+                     locus const &loc,
+                     uint64_t start, uint64_t end)
+{
+  wr_format_padding_message (category | mc_acc_bloat | mc_impact_1,
+                            loc, start, end,
+                            "unnecessary padding with zero bytes");
+}
+
+void
+wr_message_padding_n0 (message_category category,
+                      locus const &loc,
+                      uint64_t start, uint64_t end)
+{
+  wr_format_padding_message (category | mc_acc_bloat | mc_impact_1,
+                            loc, start, end,
+                            "unreferenced non-zero bytes");
+}
+
+void
+wr_reset_counters ()
+{
+  error_count = 0;
+  message_count_filter::inst ()->clear ();
+}
+
+bool
+wr_prev_emitted ()
+{
+  return message_context::_m_last_emitted;
+}
diff --git a/dwarflint/messages.hh b/dwarflint/messages.hh
new file mode 100644 (file)
index 0000000..7a5828a
--- /dev/null
@@ -0,0 +1,233 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_MESSAGES_HH
+#define DWARFLINT_MESSAGES_HH
+
+#include "locus.hh"
+#include "libdw.h"
+#include <string>
+#include <iosfwd>
+#include <vector>
+#include <sstream>
+#include <map>
+
+#define MESSAGE_CATEGORIES                                             \
+  /* Severity: */                                                      \
+  MC (impact_1,  0)  /* no impact on the consumer */                   \
+  MC (impact_2,  1)  /* still no impact, but suspicious or worth mentioning */ \
+  MC (impact_3,  2)  /* some impact */                                 \
+  MC (impact_4,  3)  /* high impact */                                 \
+                                                                       \
+  /* Accuracy:  */                                                     \
+  MC (acc_bloat, 4)  /* unnecessary constructs (e.g. unreferenced strings) */ \
+  MC (acc_suboptimal, 5) /* suboptimal construct (e.g. lack of siblings) */ \
+                                                                       \
+  /* Various: */                                                       \
+  MC (error,     6)      /* turn the message into an error */          \
+                                                                       \
+  /* Area: */                                                          \
+  MC (leb128,    7)  /* ULEB/SLEB storage */                           \
+  MC (abbrevs,   8)  /* abbreviations and abbreviation tables */       \
+  MC (die_rel,   9)  /* DIE relationship */                            \
+  MC (die_other, 10) /* other messages related to DIEs */              \
+  MC (info,      11) /* messages related to .debug_info, but not particular DIEs */ \
+  MC (strings,   12) /* string table */                                        \
+  MC (aranges,   13) /* address ranges table */                                \
+  MC (elf,       14) /* ELF structure, e.g. missing optional sections */ \
+  MC (pubtables, 15) /* table of public names/types */                 \
+  MC (pubtypes,  16) /* .debug_pubtypes presence */                    \
+  MC (loc,       17) /* messages related to .debug_loc */              \
+  MC (ranges,    18) /* messages related to .debug_ranges */           \
+  MC (line,      19) /* messages related to .debug_line */             \
+  MC (reloc,     20) /* messages related to relocation handling */     \
+  MC (header,    21) /* messages related to header portions in general */ \
+  MC (mac,       22) /* messages related to .debug_mac */              \
+  MC (other,     31) /* messages unrelated to any of the above */
+
+enum message_category
+  {
+    mc_none      = 0,
+
+#define MC(CAT, ID)                            \
+    mc_##CAT = 1u << ID,
+    MESSAGE_CATEGORIES
+#undef MC
+  };
+
+message_category operator | (message_category a, message_category b);
+message_category &operator |= (message_category &a, message_category b);
+std::ostream &operator<< (std::ostream &o, message_category cat);
+
+struct message_term
+{
+  /* Given a term like A && !B && C && !D, we decompose it thus: */
+  message_category positive; /* non-zero bits for plain predicates */
+  message_category negative; /* non-zero bits for negated predicates */
+
+  message_term (message_category pos, message_category neg = mc_none)
+    : positive (pos), negative (neg)
+  {}
+
+  std::string str () const;
+};
+
+std::ostream &operator<< (std::ostream &o, message_term const &term);
+
+struct message_criteria
+  : protected std::vector<message_term>
+{
+  using std::vector<message_term>::at;
+  using std::vector<message_term>::size;
+
+  void operator |= (message_term const &term);
+  void operator &= (message_term const &term);
+  void operator *= (message_criteria const &term);
+  void and_not (message_term const &term);
+
+  std::string str () const;
+};
+
+std::ostream &operator<< (std::ostream &o, message_criteria const &criteria);
+
+message_criteria operator ! (message_term const &);
+
+extern void wr_error (locus const *wh, const char *format, ...)
+  __attribute__ ((format (printf, 2, 3)));
+
+extern void wr_message (unsigned long category, locus const *loc,
+                       const char *format, ...)
+  __attribute__ ((format (printf, 3, 4)));
+
+extern void wr_format_padding_message (message_category category,
+                                      locus const &loc,
+                                      uint64_t start, uint64_t end,
+                                      char const *kind);
+
+extern void wr_format_leb128_message (locus const &loc,
+                                     const char *what,
+                                     const char *purpose,
+                                     const unsigned char *begin,
+                                     const unsigned char *end);
+
+extern void wr_message_padding_0 (message_category category,
+                                 locus const &loc,
+                                 uint64_t start, uint64_t end);
+
+extern void wr_message_padding_n0 (message_category category,
+                                  locus const &loc,
+                                  uint64_t start, uint64_t end);
+
+extern bool message_accept (struct message_criteria const *cri,
+                           unsigned long cat);
+
+
+extern unsigned error_count;
+
+/* Messages that are accepted (and made into warning).  */
+extern struct message_criteria warning_criteria;
+
+/* Accepted (warning) messages, that are turned into errors.  */
+extern struct message_criteria error_criteria;
+
+class message_count_filter
+{
+  struct counter
+  {
+    unsigned value;
+    counter () : value (0) {}
+    unsigned operator++ () { return ++value; }
+  };
+
+  typedef std::map<void const *, counter> counters_t;
+
+  // NULL for filtered-out message, otherwise array of <key, count>
+  // pairs sorted by key.
+  counters_t _m_counters;
+  friend void wr_reset_counters ();
+
+  void
+  clear ()
+  {
+    counters_t empty;
+    _m_counters.swap (empty);
+  }
+
+public:
+
+  int should_emit (void const *key);
+  static message_count_filter *
+  inst ()
+  {
+    static message_count_filter inst;
+    return &inst;
+  }
+};
+
+class message_context
+{
+  static bool _m_last_emitted;
+
+  message_count_filter *_m_filter;
+  locus const *_m_loc;
+  char const *_m_prefix;
+
+  friend message_context wr_message (locus const &loc, message_category cat);
+  friend message_context wr_message (message_category cat);
+  friend bool wr_prev_emitted ();
+
+  message_context (message_count_filter *filter,
+                  locus const *loc, char const *prefix);
+
+public:
+  static message_context filter_message (locus const *loc,
+                                        message_category category);
+
+  std::ostream &operator << (char const *message);
+  std::ostream &operator << (std::string const &message);
+
+  template<class T>
+  std::ostream &
+  operator << (T const &t)
+  {
+    std::stringstream ss;
+    ss << t;
+    return (*this) << ss.str ();
+  }
+
+  // Use KEY for count filtering.
+  std::ostream &id (void const *key);
+
+  // Use KEY for count filtering.  WHETHER is true if the message will
+  // be emitted.  It doesn't touch that value otherwise, so WHETHER
+  // must be pre-initialized to false.
+  std::ostream &id (void const *key, bool &whether);
+
+  // Return either the full stream, or a sink, depending on WHETHER.
+  std::ostream &when (bool whether) const;
+
+  std::ostream &when_prev () const;
+};
+
+std::ostream &wr_error (locus const &loc);
+std::ostream &wr_error ();
+message_context wr_message (locus const &loc, message_category cat);
+message_context wr_message (message_category cat);
+void wr_reset_counters ();
+bool wr_prev_emitted ();
+
+#endif//DWARFLINT_MESSAGES_HH
diff --git a/dwarflint/misc.cc b/dwarflint/misc.cc
new file mode 100644 (file)
index 0000000..114d0d9
--- /dev/null
@@ -0,0 +1,60 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2008, 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "misc.hh"
+#include "messages.hh"
+#include <stdarg.h>
+
+bool
+address_aligned (uint64_t addr, uint64_t align)
+{
+  return align < 2 || (addr % align == 0);
+}
+
+bool
+necessary_alignment (uint64_t start, uint64_t length, uint64_t align)
+{
+  return address_aligned (start + length, align) && length < align;
+}
+
+bool
+supported_version (unsigned version,
+                  size_t num_supported, locus const &loc, ...)
+{
+  bool retval = false;
+  va_list ap;
+  va_start (ap, loc);
+  for (size_t i = 0; i < num_supported; ++i)
+    {
+      unsigned v = va_arg (ap, unsigned);
+      if (version == v)
+       {
+         retval = true;
+         break;
+       }
+    }
+  va_end (ap);
+
+  if (!retval)
+    wr_error (loc) << "unsupported version " << version << ".\n";
+
+  return retval;
+}
diff --git a/dwarflint/misc.hh b/dwarflint/misc.hh
new file mode 100644 (file)
index 0000000..f779ed6
--- /dev/null
@@ -0,0 +1,54 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_MISC_HH
+#define DWARFLINT_MISC_HH
+
+#include <cstring>
+#include "locus.hh"
+
+extern "C"
+{
+#include "../lib/system.h"
+}
+
+#define REALLOC(A, BUF)                                        \
+  do {                                                 \
+    typeof ((A)) _a = (A);                             \
+    if (_a->size == _a->alloc)                         \
+      {                                                        \
+       if (_a->alloc == 0)                             \
+         _a->alloc = 8;                                \
+       else                                            \
+         _a->alloc *= 2;                               \
+       _a->BUF = (typeof (_a->BUF))                    \
+         xrealloc (_a->BUF,                            \
+                   sizeof (*_a->BUF) * _a->alloc);     \
+      }                                                        \
+  } while (0)
+
+bool address_aligned (uint64_t addr, uint64_t align);
+bool necessary_alignment (uint64_t start, uint64_t length,
+                         uint64_t align);
+
+bool supported_version (unsigned version,
+                       size_t num_supported, locus const &loc, ...);
+
+#define UNREACHABLE assert (!"unreachable")
+
+
+#endif//DWARFLINT_MISC_HH
diff --git a/dwarflint/option.cc b/dwarflint/option.cc
new file mode 100644 (file)
index 0000000..7248258
--- /dev/null
@@ -0,0 +1,223 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "option.hh"
+#include "dwarflint.hh"
+#include "checkdescriptor.hh"
+#include "check_registrar.hh"
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+argppp *argppp::instance = NULL;
+
+option_i *
+options::find_opt (int key) const
+{
+  const_iterator it = find (key);
+  if (it == end ())
+    return NULL;
+  return it->second;
+}
+
+option_i const *
+options::getopt (int key) const
+{
+  return find_opt (key);
+}
+
+struct last_option
+  : public argp_option
+{
+  last_option ()
+  {
+    std::memset (this, 0, sizeof (*this));
+  }
+};
+
+void
+options::add (option_i *opt)
+{
+  int key = opt->key ();
+  assert (getopt (key) == NULL);
+  (*this)[key] = opt;
+}
+
+/* Bug report address.  */
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+argp
+options::build_argp (bool toplev) const
+{
+  _m_opts.clear ();
+  for (const_iterator it = begin (); it != end (); ++it)
+    _m_opts.push_back (it->second->build_option ());
+  _m_opts.push_back (last_option ());
+  argp a = {
+    &_m_opts.front (),
+    NULL, // needs to be initialized later, in argppp
+    !toplev ? NULL : "FILE...",
+    !toplev ? NULL : "\
+Pedantic checking of DWARF stored in ELF files.",
+    NULL, NULL, NULL
+  };
+  return a;
+}
+
+argppp::argppp (options const &global)
+  : _m_inited (false)
+{
+  argp main = global.build_argp (true);
+  main.parser = &parse_opt;
+  _m_argp = main;
+
+  // Only one instance is allowed per program.
+  assert (instance == NULL);
+  instance = this;
+}
+
+argppp::argppp (options const &global,
+               std::vector<checkdescriptor const *> checkdescriptors)
+  : _m_inited (false)
+{
+  argp main = global.build_argp (true);
+
+  typedef main_check_registrar::checkdescriptors_t checkdescriptors_t;
+  for (checkdescriptors_t::const_iterator it = checkdescriptors.begin ();
+       it != checkdescriptors.end (); ++it)
+    if (!(*it)->opts ().empty ())
+      {
+       _m_children_argps.push_back ((*it)->opts ().build_argp ());
+       _m_children_argps.back ().parser = &parse_opt;
+       _m_children_headers.push_back (std::string ("Options for ")
+                                      + (*it)->name ()
+                                      + ":");
+       _m_children_inputs.push_back (&(*it)->opts ());
+      }
+
+  unsigned pos = 0;
+  for (checkdescriptors_t::const_iterator it = checkdescriptors.begin ();
+       it != checkdescriptors.end (); ++it)
+    if (!(*it)->opts ().empty ())
+      {
+       argp_child child = {&_m_children_argps[pos], 0,
+                           _m_children_headers[pos].c_str (), 0};
+       _m_children.push_back (child);
+       pos++;
+      }
+  assert (_m_children_argps.size () == _m_children.size ());
+
+  if (!_m_children.empty ())
+    {
+      _m_children.push_back ((argp_child){NULL, 0, NULL, 0});
+      main.children = &_m_children.front ();
+    }
+
+  main.parser = &parse_opt;
+  _m_argp = main;
+
+  // Only one instance is allowed per program.
+  assert (instance == NULL);
+  instance = this;
+}
+
+error_t
+argppp::parse_opt (int key, char *arg, argp_state *state)
+{
+  assert (instance != NULL);
+  if (key == ARGP_KEY_INIT && !instance->_m_inited)
+    {
+      instance->_m_inited = true;
+      unsigned i = 0;
+      for (std::vector<options const *>::const_iterator it
+            = instance->_m_children_inputs.begin ();
+          it != instance->_m_children_inputs.end (); ++it)
+       state->child_inputs[i++] = const_cast<options *> (*it);
+      return 0;
+    }
+  else
+    {
+      assert (state->input != NULL);
+      options const *opts = static_cast<options const *> (state->input);
+      option_i *o = opts->find_opt (key);
+      if (o == NULL)
+       return ARGP_ERR_UNKNOWN;
+      return o->parse_opt (arg, state);
+    }
+}
+
+void
+argppp::parse (int argc, char **argv, unsigned flags, int *remaining)
+{
+  assert (!_m_inited);
+  argp_parse (&_m_argp, argc, argv, flags, remaining, &global_opts ());
+}
+
+void
+argppp::help (FILE *stream, unsigned flags, char *name)
+{
+  argp_help (&_m_argp, stream, flags, name);
+}
+
+int option_i::_m_last_opt = 300;
+
+int
+option_i::get_short_option (char opt_short)
+{
+  if (opt_short)
+    return opt_short;
+  return _m_last_opt++;
+}
+
+namespace
+{
+  argp_option argp_option_ctor (char const *name, int key,
+                               char const *arg, int flags,
+                               char const *doc, int group)
+  {
+    assert (name != NULL);
+    assert (doc != NULL);
+    argp_option opt = {
+      name, key, arg, flags, doc, group
+    };
+    return opt;
+  }
+}
+
+option_common::option_common (char const *description,
+                             char const *arg_description,
+                             char const *opt_long, char opt_short,
+                             int flags)
+  : _m_opt (argp_option_ctor (opt_long, get_short_option (opt_short),
+                             arg_description, flags,
+                             description, 0))
+  , _m_seen (false)
+{}
+
+// Trick to make sure the static options are always initialized
+// before access (it is used from various global initializers.
+
+options &
+global_opts ()
+{
+  static options inst;
+  return inst;
+}
diff --git a/dwarflint/option.hh b/dwarflint/option.hh
new file mode 100644 (file)
index 0000000..2d3e427
--- /dev/null
@@ -0,0 +1,234 @@
+/* Pedantic checker for DWARF files
+   Copyright (C) 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_OPTION_HH
+#define DWARFLINT_OPTION_HH
+
+#include <string>
+#include <argp.h>
+#include <map>
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include "option_i.hh"
+#include "checkdescriptor_i.hh"
+
+class options
+  : private std::map<int, option_i *>
+{
+  friend class argppp;
+  mutable std::vector<argp_option> _m_opts;
+
+  option_i *find_opt (int key) const;
+
+public:
+  option_i const *getopt (int key) const;
+  argp build_argp (bool toplev = false) const;
+  void add (option_i *opt);
+  using std::map<int, option_i *>::empty;
+  using std::map<int, option_i *>::begin;
+  using std::map<int, option_i *>::end;
+  using std::map<int, option_i *>::const_iterator;
+};
+
+// Wrapper of argp parsing.  While in general argp does a decent job,
+// it's not possible to pass user arguments to the parser function
+// from the argp structure itself, and therefore share the same parser
+// for all the children.  Since that's what we need to do, we need to
+// pass the "input" argument to argp_parse, and carefully setup the
+// child_inputs arguments.  That means coordinating the whole parsing
+// process from one place.  As a result this is hardly a nice,
+// reusable piece of code.
+class argppp
+{
+  std::vector<argp> _m_children_argps;
+  std::vector<std::string> _m_children_headers;
+  std::vector<argp_child> _m_children;
+  std::vector<options const *> _m_children_inputs;
+  argp _m_argp;
+  bool _m_inited;
+
+  static error_t parse_opt (int key, char *arg, argp_state *state);
+  static argppp *instance;
+
+public:
+  argppp (options const &global);
+  argppp (options const &global,
+         std::vector<checkdescriptor const *> checkdescriptors);
+
+  void parse (int argc, char **argv, unsigned flags, int *remaining);
+  void help (FILE *stream, unsigned flags, char *name);
+};
+
+class option_i // we cannot call it simply "option", this conflicts
+              // with another global declaration
+{
+  // last allocated option unique identifier
+  static int _m_last_opt;
+
+protected:
+  // Answer either opt_short argument if it's non-0.  Otherwise create
+  // new unique identifier.
+  static int get_short_option (char opt_short);
+
+public:
+  virtual ~option_i () {}
+
+  virtual bool seen () const = 0;
+  virtual argp_option const &build_option () const = 0;
+  virtual error_t parse_opt (char *arg, argp_state *state) = 0;
+  virtual int key () const = 0;
+};
+
+class option_common
+  : public option_i
+{
+  argp_option _m_opt;
+
+protected:
+  bool _m_seen;
+
+  option_common (char const *description,
+                char const *arg_description,
+                char const *opt_long, char opt_short,
+                int flags = 0);
+
+public:
+  bool
+  seen () const
+  {
+    return _m_seen;
+  }
+
+  argp_option const &
+  build_option () const
+  {
+    return _m_opt;
+  }
+
+  int
+  key () const
+  {
+    return _m_opt.key;
+  }
+};
+
+template<class arg_type>
+class value_converter;
+
+template<class arg_type>
+class xoption
+  : public option_common
+{
+  arg_type _m_arg;
+
+public:
+  xoption (char const *description,
+          char const *arg_description,
+          char const *opt_long, char opt_short = 0,
+          int flags = 0)
+    : option_common (description, arg_description, opt_long, opt_short, flags)
+  {
+  }
+
+  arg_type const &value () const
+  {
+    return _m_arg;
+  }
+
+  arg_type const &value (arg_type arg)
+  {
+    return seen () ? _m_arg : arg;
+  }
+
+  error_t parse_opt (char *arg, __attribute__ ((unused)) argp_state *state)
+  {
+    _m_seen = true;
+    _m_arg = value_converter<arg_type>::convert (arg);
+    return 0;
+  }
+};
+
+template<>
+class xoption<void>
+  : public option_common
+{
+public:
+  xoption (char const *description,
+          char const *opt_long, char opt_short = 0, int flags = 0)
+    : option_common (description, NULL, opt_long, opt_short, flags)
+  {
+  }
+
+  error_t parse_opt (char *arg, __attribute__ ((unused)) argp_state *state)
+  {
+    assert (arg == NULL);
+    _m_seen = true;
+    return 0;
+  }
+
+  // This shouldn't be promoted to option_common, as
+  // e.g. xoption<bool> will naturally have a different
+  // implementation.
+  operator bool () { return seen (); }
+};
+
+template<>
+struct value_converter<std::string>
+{
+  static std::string convert (char const *arg)
+  {
+    if (arg == NULL)
+      return "";
+    else
+      return arg;
+  }
+};
+
+template<>
+struct value_converter<unsigned>
+{
+  static unsigned convert (char const *arg)
+  {
+    unsigned u;
+    if (std::sscanf (arg, "%u", &u) == 1)
+      return u;
+    else
+      return -1;
+  }
+};
+
+typedef xoption<void> void_option;
+typedef xoption<std::string> string_option;
+typedef xoption<unsigned> unsigned_option;
+
+options & global_opts ();
+
+template<class OPT>
+struct global_opt
+  : public OPT
+{
+  template<typename... Args>
+  global_opt (Args const&... args)
+    : OPT (args...)
+  {
+    global_opts ().add (this);
+  }
+};
+
+#endif//DWARFLINT_OPTION_HH
diff --git a/dwarflint/option_i.hh b/dwarflint/option_i.hh
new file mode 100644 (file)
index 0000000..473d62a
--- /dev/null
@@ -0,0 +1 @@
+class option_i;
diff --git a/dwarflint/pri.cc b/dwarflint/pri.cc
new file mode 100644 (file)
index 0000000..fc01542
--- /dev/null
@@ -0,0 +1,65 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2008,2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sstream>
+
+#include "../src/dwarfstrings.h"
+#include "../libdw/c++/dwarf"
+
+#include "pri.hh"
+
+std::ostream &
+pri::operator << (std::ostream &os, pri::pribase const &obj)
+{
+  return os << obj.m_s;
+}
+
+std::ostream &
+pri::operator << (std::ostream &os, pri::ref const &obj)
+{
+  std::stringstream ss;
+  ss << std::hex << "DIE " << obj.off;
+  return os << ss.str ();
+}
+
+std::ostream &
+pri::operator << (std::ostream &os, pri::hex const &obj)
+{
+  std::stringstream ss;
+  if (obj.pre)
+    ss << obj.pre << " ";
+  ss << std::hex << "0x" << obj.value;
+  return os << ss.str ();
+}
+
+std::ostream &
+pri::operator << (std::ostream &os, pri::range const &obj)
+{
+  return os << "[" << pri::addr (obj.start)
+           << ", " << pri::addr (obj.end) << ")";
+}
+
+std::string
+pri::attr_name (int name)
+{
+  assert (name != -1);
+  return elfutils::dwarf::attributes::name (name);
+}
diff --git a/dwarflint/pri.hh b/dwarflint/pri.hh
new file mode 100644 (file)
index 0000000..a1bb8ef
--- /dev/null
@@ -0,0 +1,113 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_PRI_H
+#define DWARFLINT_PRI_H
+
+#include "../libdw/libdw.h"
+#include <string>
+
+#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
+
+namespace pri
+{
+  class pribase
+  {
+    std::string m_s;
+
+  protected:
+    pribase (std::string const &a,
+            std::string const &b = "",
+            std::string const &c = "")
+      : m_s (a + b + c)
+    {}
+    friend std::ostream &operator << (std::ostream &os, pribase const &obj);
+
+  public:
+    operator std::string const &() const { return m_s; }
+  };
+  std::ostream &operator << (std::ostream &os, pribase const &obj);
+
+  struct not_enough
+    : public pribase
+  {
+    not_enough (std::string const &what)
+      : pribase ("not enough data for ", what)
+    {}
+  };
+
+  struct lacks_relocation
+    : public pribase
+  {
+    lacks_relocation (std::string const &what)
+      : pribase (what, " seems to lack a relocation")
+    {}
+  };
+
+  class ref
+  {
+    Dwarf_Off off;
+  public:
+    template <class T>
+    ref (T const &die)
+      : off (die.offset ())
+    {}
+    friend std::ostream &operator << (std::ostream &os, ref const &obj);
+  };
+  std::ostream &operator << (std::ostream &os, ref const &obj);
+
+  class hex
+  {
+    Dwarf_Off value;
+    char const *const pre;
+  public:
+    hex (Dwarf_Off a_value, char const *a_pre = NULL)
+      : value (a_value)
+      , pre (a_pre)
+    {}
+    friend std::ostream &operator << (std::ostream &os, hex const &obj);
+  };
+  std::ostream &operator << (std::ostream &os, hex const &obj);
+
+  struct addr: public hex {
+    addr (Dwarf_Off off) : hex (off) {}
+  };
+
+  struct DIE: public hex {
+    DIE (Dwarf_Off off) : hex (off, "DIE ") {}
+  };
+
+  struct CU: public hex {
+    CU (Dwarf_Off off) : hex (off, "CU ") {}
+  };
+
+  class range
+  {
+    Dwarf_Off start;
+    Dwarf_Off end;
+  public:
+    range (Dwarf_Off a_start, Dwarf_Off a_end)
+      : start (a_start), end (a_end)
+    {}
+    friend std::ostream &operator << (std::ostream &os, range const &obj);
+  };
+  std::ostream &operator << (std::ostream &os, range const &obj);
+
+  std::string attr_name (int name);
+}
+
+#endif//DWARFLINT_PRI_H
diff --git a/dwarflint/readctx.cc b/dwarflint/readctx.cc
new file mode 100644 (file)
index 0000000..c38eb39
--- /dev/null
@@ -0,0 +1,342 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2009, 2010 Red Hat, Inc.
+   This file is part of elfutils.
+   Written by Petr Machata <pmachata@redhat.com>, 2009.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "readctx.hh"
+#include "../libdw/dwarf.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <inttypes.h>
+
+/* read_Xubyte_* is basically cut'n'paste from memory-access.h.  */
+union unaligned
+  {
+    void *p;
+    uint16_t u2;
+    uint32_t u4;
+    uint64_t u8;
+    int16_t s2;
+    int32_t s4;
+    int64_t s8;
+  } __attribute__ ((packed));
+
+static uint16_t
+read_2ubyte_unaligned (const void *p, bool other_byte_order)
+{
+  const union unaligned *up = (const union unaligned *)p;
+  if (other_byte_order)
+    return bswap_16 (up->u2);
+  return up->u2;
+}
+
+/* Prefix with dwarflint_ for export, so that it doesn't get confused
+   with functions and macros in memory-access.h.  */
+uint32_t
+dwarflint_read_4ubyte_unaligned (const void *p, bool other_byte_order)
+{
+  const union unaligned *up = (const union unaligned *)p;
+  if (other_byte_order)
+    return bswap_32 (up->u4);
+  return up->u4;
+}
+
+uint64_t
+dwarflint_read_8ubyte_unaligned (const void *p, bool other_byte_order)
+{
+  const union unaligned *up = (const union unaligned *)p;
+  if (other_byte_order)
+    return bswap_64 (up->u8);
+  return up->u8;
+}
+
+
+#define read_2ubyte_unaligned_inc(Addr, OtherByteOrder)                        \
+  ({ uint16_t t_ = read_2ubyte_unaligned (Addr, OtherByteOrder);       \
+    Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2);               \
+    t_; })
+
+#define read_4ubyte_unaligned_inc(Addr, OtherByteOrder)                        \
+  ({ uint32_t t_ = dwarflint_read_4ubyte_unaligned (Addr, OtherByteOrder); \
+    Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4);               \
+    t_; })
+
+#define read_8ubyte_unaligned_inc(Addr, OtherByteOrder)                        \
+  ({ uint64_t t_ = dwarflint_read_8ubyte_unaligned (Addr, OtherByteOrder); \
+    Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8);               \
+    t_; })
+
+
+
+void
+read_ctx_init (struct read_ctx *ctx, Elf_Data *data, bool other_byte_order)
+{
+  if (data == NULL)
+    abort ();
+
+  ctx->data = data;
+  ctx->begin = (const unsigned char *)data->d_buf;
+  ctx->end = (const unsigned char *)data->d_buf + data->d_size;
+  ctx->ptr = (const unsigned char *)data->d_buf;
+  ctx->other_byte_order = other_byte_order;
+}
+
+bool
+read_ctx_init_sub (struct read_ctx *ctx, struct read_ctx *parent,
+                  const unsigned char *begin, const unsigned char *end)
+{
+  if (parent == NULL)
+    abort ();
+
+  if (begin < parent->begin
+      || end > parent->end)
+    return false;
+
+  ctx->data = parent->data;
+  ctx->begin = begin;
+  ctx->end = end;
+  ctx->ptr = begin;
+  ctx->other_byte_order = parent->other_byte_order;
+  return true;
+}
+
+uint64_t
+read_ctx_get_offset (struct read_ctx *ctx)
+{
+  assert (ctx->ptr >= ctx->begin);
+  return (uint64_t)(ctx->ptr - ctx->begin);
+}
+
+bool
+read_ctx_need_data (struct read_ctx *ctx, size_t length)
+{
+  const unsigned char *ptr = ctx->ptr + length;
+  return ptr <= ctx->end && (length == 0 || ptr > ctx->ptr);
+}
+
+bool
+read_ctx_read_ubyte (struct read_ctx *ctx, unsigned char *ret)
+{
+  if (!read_ctx_need_data (ctx, 1))
+    return false;
+  if (ret != NULL)
+    *ret = *ctx->ptr;
+  ctx->ptr++;
+  return true;
+}
+
+int
+read_ctx_read_uleb128 (struct read_ctx *ctx, uint64_t *ret)
+{
+  uint64_t result = 0;
+  int shift = 0;
+  int size = 8 * sizeof (result);
+  bool zero_tail = false;
+
+  while (1)
+    {
+      uint8_t byte;
+      if (!read_ctx_read_ubyte (ctx, &byte))
+       return -1;
+
+      uint8_t payload = byte & 0x7f;
+      zero_tail = payload == 0 && shift > 0;
+      result |= (uint64_t)payload << shift;
+      shift += 7;
+      if (shift > size && byte != 0x1)
+       return -1;
+      if ((byte & 0x80) == 0)
+       break;
+    }
+
+  if (ret != NULL)
+    *ret = result;
+  return zero_tail ? 1 : 0;
+}
+
+int
+read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret)
+{
+  int64_t result = 0;
+  int shift = 0;
+  int size = 8 * sizeof (result);
+  bool zero_tail = false;
+  bool sign = false;
+
+  while (1)
+    {
+      uint8_t byte;
+      if (!read_ctx_read_ubyte (ctx, &byte))
+       return -1;
+
+      uint8_t payload = byte & 0x7f;
+      zero_tail = shift > 0 && ((payload == 0x7f && sign)
+                               || (payload == 0 && !sign));
+      sign = (byte & 0x40) != 0; /* Set sign for rest of loop & next round.  */
+      result |= (int64_t)payload << shift;
+      shift += 7;
+      if ((byte & 0x80) == 0)
+       {
+         if (shift < size && sign)
+           result |= -((int64_t)1 << shift);
+         break;
+       }
+      if (shift > size)
+       return -1;
+    }
+
+  if (ret != NULL)
+    *ret = result;
+  return zero_tail ? 1 : 0;
+}
+
+bool
+read_ctx_read_2ubyte (struct read_ctx *ctx, uint16_t *ret)
+{
+  if (!read_ctx_need_data (ctx, 2))
+    return false;
+  uint16_t val = read_2ubyte_unaligned_inc (ctx->ptr, ctx->other_byte_order);
+  if (ret != NULL)
+    *ret = val;
+  return true;
+}
+
+bool
+read_ctx_read_4ubyte (struct read_ctx *ctx, uint32_t *ret)
+{
+  if (!read_ctx_need_data (ctx, 4))
+    return false;
+  uint32_t val = read_4ubyte_unaligned_inc (ctx->ptr, ctx->other_byte_order);
+  if (ret != NULL)
+    *ret = val;
+  return true;
+}
+
+bool
+read_ctx_read_8ubyte (struct read_ctx *ctx, uint64_t *ret)
+{
+  if (!read_ctx_need_data (ctx, 8))
+    return false;
+  uint64_t val = read_8ubyte_unaligned_inc (ctx->ptr, ctx->other_byte_order);
+  if (ret != NULL)
+    *ret = val;
+  return true;
+}
+
+bool
+read_ctx_read_offset (struct read_ctx *ctx, bool dwarf64, uint64_t *ret)
+{
+  if (dwarf64)
+    return read_ctx_read_8ubyte (ctx, ret);
+
+  uint32_t v;
+  if (!read_ctx_read_4ubyte (ctx, &v))
+    return false;
+
+  if (ret != NULL)
+    *ret = (uint64_t)v;
+  return true;
+}
+
+bool
+read_ctx_read_var (struct read_ctx *ctx, int width, uint64_t *ret)
+{
+  switch (width)
+    {
+    case 4:
+    case 8:
+      return read_ctx_read_offset (ctx, width == 8, ret);
+    case 2:
+      {
+       uint16_t val;
+       if (!read_ctx_read_2ubyte (ctx, &val))
+         return false;
+       *ret = val;
+       return true;
+      }
+    case 1:
+      {
+       uint8_t val;
+       if (!read_ctx_read_ubyte (ctx, &val))
+         return false;
+       *ret = val;
+       return true;
+      }
+    default:
+      return false;
+    };
+}
+
+const char *
+read_ctx_read_str (struct read_ctx *ctx)
+{
+  const char *ret = (const char *)ctx->ptr;
+  uint8_t byte;
+  do
+    if (!read_ctx_read_ubyte (ctx, &byte))
+      return NULL;
+  while (byte != 0);
+  return ret;
+}
+
+bool
+read_ctx_skip (struct read_ctx *ctx, uint64_t len)
+{
+  if (!read_ctx_need_data (ctx, len))
+    return false;
+  ctx->ptr += len;
+  return true;
+}
+
+bool
+read_ctx_eof (struct read_ctx *ctx)
+{
+  return !read_ctx_need_data (ctx, 1);
+}
+
+static void
+update_off (struct read_ctx *ctx,
+           uint64_t *ret_off)
+{
+  if (ret_off != NULL)
+    *ret_off = (uint64_t)(ctx->ptr - ctx->begin);
+}
+
+bool
+read_check_zero_padding (struct read_ctx *ctx,
+                        uint64_t *ret_off_start,
+                        uint64_t *ret_off_end)
+{
+  assert (ctx->ptr != ctx->end);
+  update_off (ctx, ret_off_start);
+  bool ret = true;
+
+  const unsigned char *save_ptr = ctx->ptr;
+  while (!read_ctx_eof (ctx))
+    if (*ctx->ptr++ != 0)
+      {
+       ctx->ptr = save_ptr;
+       goto done;
+      }
+
+ done:
+  update_off (ctx, ret_off_end);
+  return ret;
+}
diff --git a/dwarflint/readctx.hh b/dwarflint/readctx.hh
new file mode 100644 (file)
index 0000000..95033ca
--- /dev/null
@@ -0,0 +1,73 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009, 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef dwarflint_readctx_h
+#define dwarflint_readctx_h
+
+#include <stdbool.h>
+#include "../libelf/libelf.h"
+
+/* Functions and data structures related to bounds-checked
+   reading.  */
+
+struct read_ctx
+{
+  Elf_Data *data;
+  const unsigned char *ptr;
+  const unsigned char *begin;
+  const unsigned char *end;
+  bool other_byte_order;
+};
+
+uint32_t dwarflint_read_4ubyte_unaligned (const void *p,
+                                         bool other_byte_order);
+uint64_t dwarflint_read_8ubyte_unaligned (const void *p,
+                                         bool other_byte_order);
+
+
+void read_ctx_init (struct read_ctx *ctx,
+                   Elf_Data *data,
+                   bool other_byte_order);
+bool read_ctx_init_sub (struct read_ctx *ctx,
+                       struct read_ctx *parent,
+                       const unsigned char *begin,
+                       const unsigned char *end);
+uint64_t read_ctx_get_offset (struct read_ctx *ctx);
+bool read_ctx_need_data (struct read_ctx *ctx, size_t length);
+bool read_ctx_read_ubyte (struct read_ctx *ctx, unsigned char *ret);
+int read_ctx_read_uleb128 (struct read_ctx *ctx, uint64_t *ret);
+int read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret);
+bool read_ctx_read_2ubyte (struct read_ctx *ctx, uint16_t *ret);
+bool read_ctx_read_4ubyte (struct read_ctx *ctx, uint32_t *ret);
+bool read_ctx_read_8ubyte (struct read_ctx *ctx, uint64_t *ret);
+bool read_ctx_read_offset (struct read_ctx *ctx, bool dwarf64,
+                          uint64_t *ret);
+bool read_ctx_read_var (struct read_ctx *ctx, int width, uint64_t *ret);
+const char *read_ctx_read_str (struct read_ctx *ctx);
+bool read_ctx_skip (struct read_ctx *ctx, uint64_t len);
+bool read_ctx_eof (struct read_ctx *ctx);
+
+/* See if what remains in the read context is just a zero padding.  If
+   yes, return true.  If it isn't, revert the read pointer back as if
+   nothing had happened and return false.  Furthermore, in any case,
+   if any of the ret pointers is non-NULL, it is filled, respectively,
+   with start and end offset of the zero padding run.  */
+bool read_check_zero_padding (struct read_ctx *ctx,
+                             uint64_t *ret_off_start,
+                             uint64_t *ret_off_end);
+
+#endif
diff --git a/dwarflint/reloc.cc b/dwarflint/reloc.cc
new file mode 100644 (file)
index 0000000..ec8232d
--- /dev/null
@@ -0,0 +1,510 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009,2010,2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+// xxx drop as soon as not necessary
+#define __STDC_FORMAT_MACROS
+
+#include "reloc.hh"
+#include "elf_file.hh"
+#include "messages.hh"
+#include "misc.hh"
+#include "readctx.hh"
+#include "pri.hh"
+
+#include <sstream>
+#include <libebl.h>
+#include <cassert>
+#include <cinttypes>
+
+namespace
+{
+  class reloc_locus
+    : public locus
+  {
+    locus const &_m_ref;
+    size_t _m_index;
+    uint64_t _m_offset;
+    int _m_type;
+
+  public:
+    reloc_locus (int type, locus const &ref, uint64_t offset)
+      : _m_ref (ref)
+      , _m_index (-1)
+      , _m_offset (offset)
+      , _m_type (type)
+    {
+    }
+
+    reloc_locus (int type, locus const &ref, unsigned index)
+      : _m_ref (ref)
+      , _m_index (index)
+      , _m_offset (-1)
+      , _m_type (type)
+    {
+    }
+
+    void
+    set_offset (uint64_t offset)
+    {
+      _m_offset = offset;
+    }
+
+    virtual std::string
+    format (bool) const
+    {
+      std::stringstream ss;
+      ss << (_m_type == SHT_REL ? ".rel" : ".rela") << " ";
+      if (_m_offset != (uint64_t)-1)
+       ss << pri::hex (_m_offset);
+      else
+       {
+         assert (_m_index != (size_t)-1);
+         ss << "#" << _m_index;
+       }
+
+      // Do non-brief formatting of referee
+      ss << " of " << _m_ref.format ();
+      return ss.str ();
+    }
+  };
+}
+
+relocation *
+relocation_next (relocation_data *reloc, uint64_t offset,
+                locus const &loc, enum skip_type st)
+{
+  if (reloc == NULL || reloc->rel == NULL)
+    return NULL;
+
+  while (reloc->index < reloc->size)
+    {
+      struct relocation *rel = reloc->rel + reloc->index;
+
+      /* This relocation entry is ahead of us.  */
+      if (rel->offset > offset)
+       return NULL;
+
+      reloc->index++;
+
+      if (rel->invalid)
+       continue;
+
+      if (rel->offset < offset)
+       {
+         if (st != skip_ok)
+           {
+             reloc_locus reloc_where (reloc->type, loc, rel->offset);
+             wr_error (reloc_where)
+               << (st == skip_unref
+                   ? "relocation targets unreferenced portion of the section."
+                   : (assert (st == skip_mismatched),
+                      "relocation relocates unknown datum."))
+               << std::endl;
+           }
+         continue;
+       }
+
+      return rel;
+    }
+
+  return NULL;
+}
+
+/* Skip all relocation up to offset, and leave cursor pointing at that
+   relocation, so that next time relocation_next is called, relocation
+   matching that offset is immediately yielded.  */
+void
+relocation_skip (struct relocation_data *reloc, uint64_t offset,
+                locus const &loc, enum skip_type st)
+{
+  if (reloc != NULL && reloc->rel != NULL)
+    relocation_next (reloc, offset - 1, loc, st);
+}
+
+void
+relocation_reset (struct relocation_data *reloc)
+{
+  if (reloc != NULL)
+    reloc->index = 0;
+}
+
+/* Skip all the remaining relocations.  */
+void
+relocation_skip_rest (relocation_data *reloc,
+                     locus const &loc)
+{
+  if (reloc->rel != NULL)
+    relocation_next (reloc, (uint64_t)-1, loc, skip_mismatched);
+}
+
+static void
+do_one_relocation (elf_file const *file,
+                  relocation_data *reloc,
+                  relocation *rel,
+                  unsigned rel_width,
+                  uint64_t *value,
+                  reloc_locus const &reloc_where,
+                  rel_target reltgt,
+                  GElf_Sym *symbol,
+                  GElf_Sym **symptr)
+{
+#define require(T, STREAMOPS)                  \
+  do {                                         \
+    if (!(T))                                  \
+      {                                                \
+       wr_error (reloc_where) << STREAMOPS     \
+                              << std::endl;    \
+       return;                                 \
+      }                                                \
+  } while (0)
+
+#define require_valid_section_index                                    \
+  require (section_index_valid,                                                \
+          "invalid associated section #" << section_index << '.')
+
+  symbol = gelf_getsym (reloc->symdata, rel->symndx, symbol);
+  if (symptr != NULL)
+    *symptr = symbol;
+  if (symbol == NULL)
+    {
+      wr_error (&reloc_where,
+               ": couldn't obtain symbol #%d: %s.\n",
+               rel->symndx, elf_errmsg (-1));
+      return;
+    }
+
+  GElf_Section section_index = symbol->st_shndx;
+  /* XXX We should handle SHN_XINDEX here.  Or, instead, maybe it
+     would be possible to use dwfl, which already does XINDEX
+     translation.  */
+
+  if (section_index == 0)
+    {
+       wr_error (reloc_where)
+         << "relocation refers to an undefined symbol."
+         << std::endl;
+       return;
+    }
+
+  // Valid in the sense that it can be used as an index to file->sec
+  bool section_index_valid = section_index < file->size;
+
+  /* For ET_REL files, we do section layout manually.  But we
+     don't update symbol table doing that.  So instead of looking
+     at symbol value, look at section address.  */
+  GElf_Addr sym_value = symbol->st_value;
+  if (file->ehdr.e_type == ET_REL
+      && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
+    {
+      if (sym_value != 0)
+       wr_message (reloc_where, mc_reloc | mc_impact_1)
+         << "relocation formed using STT_SECTION symbol with non-zero value."
+         << std::endl;
+
+      require_valid_section_index;
+      sym_value = file->sec[section_index].shdr.sh_addr;
+    }
+
+  /* It's target value, not section offset.  */
+  if (reltgt == rel_target::rel_value
+      || reltgt == rel_target::rel_address
+      || reltgt == rel_target::rel_exec)
+    {
+      /* If a target value is what's expected, then complain if
+        it's not either SHN_ABS, an SHF_ALLOC section, or
+        SHN_UNDEF.  For data forms of address_size, an SHN_UNDEF
+        reloc is acceptable, otherwise reject it.  */
+      if (!(section_index == SHN_ABS
+           || (reltgt == rel_target::rel_address
+               && (section_index == SHN_UNDEF
+                   || section_index == SHN_COMMON))))
+       {
+         if (reltgt != rel_target::rel_address && section_index == SHN_UNDEF)
+           wr_error (&reloc_where,
+                     ": relocation of an address is formed using SHN_UNDEF symbol"
+                     " (symtab index %d).\n", rel->symndx);
+         else
+           {
+             require_valid_section_index;
+             GElf_Shdr *shdr = &file->sec[section_index].shdr;
+             if ((shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
+               wr_message (mc_reloc | mc_impact_3, &reloc_where,
+                           ": associated section %s isn't SHF_ALLOC.\n",
+                           file->sec[section_index].name);
+             if (reltgt == rel_target::rel_exec
+                 && (shdr->sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
+               /* This may still be kosher, but it's suspicious.  */
+               wr_message (mc_reloc | mc_impact_2, &reloc_where,
+                           ": relocation against %s is suspicious, expected executable section.\n",
+                           file->sec[section_index].name);
+           }
+       }
+    }
+  else
+    {
+      require_valid_section_index;
+      section_id secid = file->sec[section_index].id;
+      if (reltgt != secid)
+       // If symtab[symndx].st_shndx does not match the expected
+       // debug section's index, complain.
+       wr_error (reloc_where)
+         << "relocation references section "
+         << (file->sec[section_index].name ?: "<invalid>") << ", but "
+         << section_locus (secid) << " was expected." << std::endl;
+    }
+
+  /* Only do the actual relocation if we have ET_REL files.  For
+     non-ET_REL files, only do the above checking.  */
+  if (file->ehdr.e_type == ET_REL)
+    {
+      *value = rel->addend + sym_value;
+      if (rel_width == 4)
+       *value = *value & (uint64_t)(uint32_t)-1;
+    }
+
+#undef require_valid_section_index
+#undef require
+}
+
+/* SYMPTR may be NULL, otherwise (**SYMPTR) has to yield valid memory
+   location.  When the function returns, (*SYMPTR) is either NULL, in
+   which case we failed or didn't get around to obtain the symbol from
+   symbol table, or non-NULL, in which case the symbol was initialized.  */
+void
+relocate_one (struct elf_file const *file,
+             struct relocation_data *reloc,
+             struct relocation *rel,
+             unsigned width, uint64_t *value,
+             locus const &loc,
+             rel_target reltgt,
+             GElf_Sym **symptr)
+{
+  if (rel->invalid)
+    return;
+
+  reloc_locus reloc_where (reloc->type, loc, rel->offset);
+
+  GElf_Sym symbol_mem, *symbol;
+  if (symptr != NULL)
+    {
+      symbol = *symptr;
+      *symptr = NULL;
+    }
+  else
+    symbol = &symbol_mem;
+
+  if (reltgt == sec_invalid)
+    {
+      wr_message (reloc_where, mc_impact_3 | mc_reloc)
+       << "relocates a datum that shouldn't be relocated.\n";
+      return;
+    }
+
+  Elf_Type type = ebl_reloc_simple_type (file->ebl, rel->type);
+
+  unsigned rel_width;
+  switch (type)
+    {
+    case ELF_T_BYTE:
+      rel_width = 1;
+      break;
+
+    case ELF_T_HALF:
+      rel_width = 2;
+      break;
+
+    case ELF_T_WORD:
+    case ELF_T_SWORD:
+      rel_width = 4;
+      break;
+
+    case ELF_T_XWORD:
+    case ELF_T_SXWORD:
+      rel_width = 8;
+      break;
+
+    default:
+      /* This has already been diagnosed during the isolated
+        validation of relocation section.  */
+      return;
+    };
+
+  if (rel_width != width)
+    wr_error (reloc_where)
+      << rel_width << "-byte relocation relocates "
+      << width << "-byte datum.\n";
+
+  // Tolerate if we failed to obtain the symbol table.
+  if (reloc->symdata != NULL)
+    do_one_relocation (file, reloc, rel, rel_width, value,
+                      reloc_where, reltgt, symbol, symptr);
+}
+
+static GElf_Rela *
+get_rel_or_rela (Elf_Data *data, int ndx,
+                GElf_Rela *dst, size_t type)
+{
+  if (type == SHT_RELA)
+    return gelf_getrela (data, ndx, dst);
+  else
+    {
+      assert (type == SHT_REL);
+      GElf_Rel rel_mem;
+      if (gelf_getrel (data, ndx, &rel_mem) == NULL)
+       return NULL;
+      dst->r_offset = rel_mem.r_offset;
+      dst->r_info = rel_mem.r_info;
+      dst->r_addend = 0;
+      return dst;
+    }
+}
+
+/* Sort the reloc section so that the applicable addresses of
+   relocation entries are monotonously increasing.  */
+static int
+compare_rel (const void *a, const void *b)
+{
+  return ((struct relocation *)a)->offset
+    - ((struct relocation *)b)->offset;
+}
+
+bool
+read_rel (struct elf_file *file,
+         struct sec *sec,
+         Elf_Data *reldata,
+         bool elf_64)
+{
+  assert (sec->rel.type == SHT_REL
+         || sec->rel.type == SHT_RELA);
+  bool is_rela = sec->rel.type == SHT_RELA;
+
+  struct read_ctx ctx;
+  read_ctx_init (&ctx, sec->data, file->other_byte_order);
+
+  size_t entrysize
+    = elf_64
+    ? (is_rela ? sizeof (Elf64_Rela) : sizeof (Elf64_Rel))
+    : (is_rela ? sizeof (Elf32_Rela) : sizeof (Elf32_Rel));
+  size_t count = reldata->d_size / entrysize;
+
+  section_locus parent (sec->id);
+
+  for (unsigned i = 0; i < count; ++i)
+    {
+      reloc_locus where (sec->rel.type, parent, i);
+
+      REALLOC (&sec->rel, rel);
+      struct relocation *cur = sec->rel.rel + sec->rel.size++;
+      new (cur) relocation ();
+
+      GElf_Rela rela_mem, *rela
+       = get_rel_or_rela (reldata, i, &rela_mem, sec->rel.type);
+      if (rela == NULL)
+       {
+         wr_error (&where, ": couldn't read relocation.\n");
+       skip:
+         cur->invalid = true;
+         continue;
+       }
+
+      int cur_type = GELF_R_TYPE (rela->r_info);
+      if (cur_type == 0) /* No relocation.  */
+       {
+         wr_message (mc_impact_3 | mc_reloc | mc_acc_bloat, &where,
+                     ": NONE relocation is superfluous.\n");
+         goto skip;
+       }
+
+      cur->offset = rela->r_offset;
+      cur->symndx = GELF_R_SYM (rela->r_info);
+      cur->type = cur_type;
+
+      where.set_offset (cur->offset);
+
+      Elf_Type type = ebl_reloc_simple_type (file->ebl, cur->type);
+      int width;
+
+      switch (type)
+       {
+       case ELF_T_WORD:
+       case ELF_T_SWORD:
+         width = 4;
+         break;
+
+       case ELF_T_XWORD:
+       case ELF_T_SXWORD:
+         width = 8;
+         break;
+
+       case ELF_T_BYTE:
+       case ELF_T_HALF:
+         /* Technically legal, but never used.  Better have dwarflint
+            flag them as erroneous, because it's more likely these
+            are a result of a bug than actually being used.  */
+         {
+           char buf[64];
+           wr_error (&where, ": 8 or 16-bit relocation type %s.\n",
+                     ebl_reloc_type_name (file->ebl, cur->type,
+                                          buf, sizeof (buf)));
+           goto skip;
+         }
+
+       default:
+         {
+           char buf[64];
+           wr_error (&where, ": invalid relocation %d (%s).\n",
+                     cur->type,
+                     ebl_reloc_type_name (file->ebl, cur->type,
+                                          buf, sizeof (buf)));
+           goto skip;
+         }
+       };
+
+      if (cur->offset + width >= sec->data->d_size)
+       {
+         wr_error (&where,
+                   ": relocation doesn't fall into relocated section.\n");
+         goto skip;
+       }
+
+      uint64_t value;
+      if (width == 4)
+       value = dwarflint_read_4ubyte_unaligned
+         ((char *)sec->data->d_buf + cur->offset, file->other_byte_order);
+      else
+       {
+         assert (width == 8);
+         value = dwarflint_read_8ubyte_unaligned
+           ((char *)sec->data->d_buf + cur->offset, file->other_byte_order);
+       }
+
+      if (is_rela)
+       {
+         if (value != 0)
+           wr_message (mc_impact_2 | mc_reloc, &where,
+                       ": SHR_RELA relocates a place with non-zero value (addend=%#"
+                       PRIx64", value=%#"PRIx64").\n", rela->r_addend, value);
+         cur->addend = rela->r_addend;
+       }
+      else
+       cur->addend = value;
+    }
+
+  qsort (sec->rel.rel, sec->rel.size,
+        sizeof (*sec->rel.rel), &compare_rel);
+  return true;
+}
diff --git a/dwarflint/reloc.hh b/dwarflint/reloc.hh
new file mode 100644 (file)
index 0000000..eda9fd9
--- /dev/null
@@ -0,0 +1,151 @@
+/* Pedantic checking of DWARF files.
+   Copyright (C) 2008, 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_RELOC_H
+#define DWARFLINT_RELOC_H
+
+#include "locus.hh"
+#include "elf_file_i.hh"
+#include <libelf.h>
+#include <gelf.h>
+
+struct relocation
+{
+  uint64_t offset;
+  uint64_t addend;
+  int symndx;
+  int type;
+  bool invalid;        /* Whether this one relocation should be
+                  ignored.  Necessary so that we don't report
+                  invalid & missing relocation twice.  */
+
+  relocation ()
+    : offset (0)
+    , addend (0)
+    , symndx (0)
+    , type (0)
+    , invalid (false)
+  {}
+};
+
+struct relocation_data
+{
+  Elf_Data *symdata;       /* Symbol table associated with this
+                             relocation section.  */
+  size_t type;             /* SHT_REL or SHT_RELA.  */
+
+  struct relocation *rel;  /* Array of relocations.  May be NULL if
+                             there are no associated relocation
+                             data.  */
+  size_t size;
+  size_t alloc;
+  size_t index;            /* Current index. */
+
+  relocation_data ()
+    : symdata (NULL)
+    , type (SHT_NULL)
+    , rel (NULL)
+    , size (0)
+    , alloc (0)
+    , index (0)
+  {}
+};
+
+enum skip_type
+  {
+    skip_unref = 0,
+    skip_mismatched = 1,
+    skip_ok,
+  };
+
+struct rel_target
+{
+  enum target
+    {
+      rel_value,       /* For relocations, this denotes that the
+                          relocation is applied to target value, not a
+                          section offset.  */
+      rel_address,     /* Same as above, but for addresses.  */
+      rel_exec,                /* Some as above, but we expect EXEC bit.  */
+    };
+
+private:
+  bool _m_is_section;
+  union
+  {
+    section_id _m_section;
+    target _m_target;
+  };
+
+public:
+  rel_target (section_id sec)
+    : _m_is_section (true)
+    , _m_section (sec)
+  {}
+
+  rel_target (target t)
+    : _m_is_section (false)
+    , _m_target (t)
+  {}
+
+  bool
+  operator== (section_id sec)
+  {
+    return _m_is_section && _m_section == sec;
+  }
+
+  bool
+  operator== (target tgt)
+  {
+    return !_m_is_section && _m_target == tgt;
+  }
+
+  template<class T>
+  bool
+  operator!= (T t)
+  {
+    return !(*this == t);
+  }
+};
+
+bool read_rel (struct elf_file *file, struct sec *sec,
+              Elf_Data *reldata, bool elf_64);
+
+relocation *relocation_next (struct relocation_data *reloc,
+                            uint64_t offset,
+                            locus const &loc,
+                            enum skip_type st);
+
+void relocation_reset (struct relocation_data *reloc);
+
+void relocation_skip (struct relocation_data *reloc, uint64_t offset,
+                     locus const &loc, enum skip_type st);
+
+void relocation_skip_rest (struct relocation_data *reloc,
+                          locus const &loc);
+
+void relocate_one (struct elf_file const *file,
+                  struct relocation_data *reloc,
+                  struct relocation *rel,
+                  unsigned width, uint64_t *value,
+                  locus const &loc,
+                  rel_target reltgt,
+                  GElf_Sym **symptr);
+
+#define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n"
+
+#endif//DWARFLINT_RELOC_H
diff --git a/dwarflint/section_id.cc b/dwarflint/section_id.cc
new file mode 100644 (file)
index 0000000..a5c857c
--- /dev/null
@@ -0,0 +1,29 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "section_id.hh"
+
+#include <stdlib.h>
+
+char const *section_name[] = {
+  "<invalid>",
+#define SEC(n) ".debug_"#n,
+  DEBUGINFO_SECTIONS
+  NULL
+#undef SEC
+};
diff --git a/dwarflint/section_id.hh b/dwarflint/section_id.hh
new file mode 100644 (file)
index 0000000..2b38f1e
--- /dev/null
@@ -0,0 +1,49 @@
+/* Pedantic checking of DWARF files
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_SECTION_ID_HH
+#define DWARFLINT_SECTION_ID_HH
+
+#define DEBUGINFO_SECTIONS \
+  SEC (info)              \
+  SEC (abbrev)            \
+  SEC (aranges)                   \
+  SEC (pubnames)          \
+  SEC (pubtypes)          \
+  SEC (str)               \
+  SEC (line)              \
+  SEC (loc)               \
+  SEC (mac)               \
+  SEC (ranges)
+
+enum section_id
+  {
+    sec_invalid = 0,
+
+    /* Debuginfo sections:  */
+#define SEC(n) sec_##n,
+    DEBUGINFO_SECTIONS
+#undef SEC
+
+    count_debuginfo_sections
+  };
+
+// section_name[0] is for sec_invalid.  The last index is for
+// count_debuginfo_sections and is NULL.
+extern char const *section_name[];
+
+#endif//DWARFLINT_SECTION_ID_HH
diff --git a/dwarflint/sections.cc b/dwarflint/sections.cc
new file mode 100644 (file)
index 0000000..f17ce53
--- /dev/null
@@ -0,0 +1,444 @@
+/* Low-level section handling.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstdlib>
+#include <cstring>
+#include "../libelf/gelf.h"
+
+#include "sections.hh"
+#include "messages.hh"
+#include "pri.hh"
+#include "misc.hh"
+
+checkdescriptor const *
+load_sections::descriptor ()
+{
+  static checkdescriptor cd
+    (checkdescriptor::create ("load_sections")
+     .hidden ());
+  return &cd;
+}
+
+checkdescriptor const *
+section_base::descriptor ()
+{
+  static checkdescriptor cd;
+  return &cd;
+}
+
+namespace
+{
+  int
+  layout_rel_file (Elf *elf)
+  {
+    GElf_Ehdr ehdr;
+    if (gelf_getehdr (elf, &ehdr) == NULL)
+      return 1;
+
+    if (ehdr.e_type != ET_REL)
+      return 0;
+
+    /* Taken from libdwfl. */
+    GElf_Addr base = 0;
+    GElf_Addr start = 0x1000, end = 0x1000, bias = 0;
+
+    bool first = true;
+    Elf_Scn *scn = NULL;
+    while ((scn = elf_nextscn (elf, scn)) != NULL)
+      {
+       GElf_Shdr shdr_mem;
+       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+       if (unlikely (shdr == NULL))
+         return 1;
+
+       if (shdr->sh_flags & SHF_ALLOC)
+         {
+           const GElf_Xword align = shdr->sh_addralign ?: 1;
+           const GElf_Addr next = (end + align - 1) & -align;
+           if (shdr->sh_addr == 0
+               /* Once we've started doing layout we have to do it all,
+                  unless we just layed out the first section at 0 when
+                  it already was at 0.  */
+               || (bias == 0 && end > start && end != next))
+             {
+               shdr->sh_addr = next;
+               if (end == base)
+                 /* This is the first section assigned a location.
+                    Use its aligned address as the module's base.  */
+                 start = base = shdr->sh_addr;
+               else if (unlikely (base & (align - 1)))
+                 {
+                   /* If BASE has less than the maximum alignment of
+                      any section, we eat more than the optimal amount
+                      of padding and so make the module's apparent
+                      size come out larger than it would when placed
+                      at zero.  So reset the layout with a better base.  */
+
+                   start = end = base = (base + align - 1) & -align;
+                   Elf_Scn *prev_scn = NULL;
+                   do
+                     {
+                       prev_scn = elf_nextscn (elf, prev_scn);
+                       GElf_Shdr prev_shdr_mem;
+                       GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
+                                                            &prev_shdr_mem);
+                       if (unlikely (prev_shdr == NULL))
+                         return 1;
+                       if (prev_shdr->sh_flags & SHF_ALLOC)
+                         {
+                           const GElf_Xword prev_align
+                             = prev_shdr->sh_addralign ?: 1;
+
+                           prev_shdr->sh_addr
+                             = (end + prev_align - 1) & -prev_align;
+                           end = prev_shdr->sh_addr + prev_shdr->sh_size;
+
+                           if (unlikely (! gelf_update_shdr (prev_scn,
+                                                             prev_shdr)))
+                             return 1;
+                         }
+                     }
+                   while (prev_scn != scn);
+                   continue;
+                 }
+
+               end = shdr->sh_addr + shdr->sh_size;
+               if (likely (shdr->sh_addr != 0)
+                   && unlikely (! gelf_update_shdr (scn, shdr)))
+                 return 1;
+             }
+           else
+             {
+               /* The address is already assigned.  Just track it.  */
+               if (first || end < shdr->sh_addr + shdr->sh_size)
+                 end = shdr->sh_addr + shdr->sh_size;
+               if (first || bias > shdr->sh_addr)
+                 /* This is the lowest address in the module.  */
+                 bias = shdr->sh_addr;
+
+               if ((shdr->sh_addr - bias + base) & (align - 1))
+                 /* This section winds up misaligned using BASE.
+                    Adjust BASE upwards to make it congruent to
+                    the lowest section address in the file modulo ALIGN.  */
+                 base = (((base + align - 1) & -align)
+                         + (bias & (align - 1)));
+             }
+
+           first = false;
+         }
+      }
+    return 0;
+  }
+
+  Elf *
+  open_elf (int fd)
+  {
+    Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+    if (unlikely (elf == NULL))
+      {
+       wr_error ()
+         << "Error opening file: " << elf_errmsg (-1) << std::endl;
+       throw check_base::failed ();
+      }
+
+    if (layout_rel_file (elf))
+      {
+       wr_error ()
+         << "Couldn't layout ET_REL file." << std::endl;
+       throw check_base::failed ();
+      }
+
+    return elf;
+  }
+
+  struct secentry
+  {
+    Elf_Data *reldata; /* Relocation data if any found.  */
+    size_t reltype;    /* SHT_REL or SHT_RELA.  We need this
+                          temporary store to be able to resolve
+                          relocation section appearing before
+                          relocated section.  */
+    size_t secndx;     /* Index into file->sec or 0 if not yet loaded.  */
+    section_id id;     /* Section type.  */
+
+    explicit secentry (section_id a_id = sec_invalid)
+      : reldata (NULL)
+      , reltype (0)
+      , secndx (0)
+      , id (a_id)
+    {}
+  };
+
+  struct secinfo_map
+    : public std::map <std::string, secentry>
+  {
+    secinfo_map ()
+    {
+      // 0 is invalid section
+      for (unsigned i = 1; i < count_debuginfo_sections; ++i)
+       (*this)[section_name[i]] = secentry (static_cast<section_id> (i));
+    }
+
+    secentry *get (const char *name)
+    {
+      iterator it = find (std::string (name));
+      if (it == end ())
+       return NULL;
+      else
+       return &it->second;
+    }
+  };
+
+  bool
+  elf_file_init (struct elf_file *file, int fd)
+  {
+    Elf *elf = open_elf (fd);
+    assert (elf != NULL);
+    memset (file, 0, sizeof (*file));
+
+    file->elf = elf;
+    file->ebl = ebl_openbackend (elf);
+
+    if (file->ebl == NULL
+       || gelf_getehdr (elf, &file->ehdr) == NULL)
+      return false;
+
+    file->addr_64 = file->ehdr.e_ident[EI_CLASS] == ELFCLASS64;
+
+    /* Taken from dwarf_begin_elf.c.  */
+    if ((BYTE_ORDER == LITTLE_ENDIAN
+        && file->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+       || (BYTE_ORDER == BIG_ENDIAN
+           && file->ehdr.e_ident[EI_DATA] == ELFDATA2LSB))
+      file->other_byte_order = true;
+
+    Elf_Scn *reloc_symtab = NULL;
+    secinfo_map secinfo;
+
+    /* Now find all necessary debuginfo sections and associated
+       relocation sections.  */
+
+    /* Section 0 is special, skip it.  */
+    REALLOC (file, sec);
+    new (file->sec + file->size++) sec ();
+
+    if (false)
+      {
+      invalid_elf:
+       wr_error () << "Broken ELF: " << elf_errmsg (-1) << "."
+                   << std::endl;
+       goto close_and_out;
+      }
+
+    /* Check that the ELF file is sound.  */
+    for (Elf_Scn *scn = NULL; (scn = elf_nextscn (elf, scn)); )
+      if (elf_rawdata (scn, NULL) == NULL)
+       goto invalid_elf;
+
+    for (Elf_Scn *scn = NULL; (scn = elf_nextscn (elf, scn)); )
+      {
+       REALLOC (file, sec);
+       size_t curndx = file->size++;
+       struct sec *cursec = file->sec + curndx;
+       new (cursec) sec ();
+
+       GElf_Shdr *shdr = gelf_getshdr (scn, &cursec->shdr);
+       if (shdr == NULL)
+         goto invalid_elf;
+
+       const char *scnname = elf_strptr (elf, file->ehdr.e_shstrndx,
+                                         shdr->sh_name);
+       // Validate the section name
+       if (scnname == NULL)
+         goto invalid_elf;
+
+       if (!address_aligned (shdr->sh_addr, shdr->sh_addralign))
+         wr_error ()
+           << "Base address of section " << scnname << ", "
+           << pri::addr (shdr->sh_addr) << ", should have an alignment of "
+           << shdr->sh_addralign << std::endl;
+
+       secentry *entry = secinfo.get (scnname);
+       cursec->scn = scn;
+       if (entry != NULL)
+         cursec->id = entry->id;
+       cursec->name = scnname;
+
+       /* Dwarf section.  */
+       if (entry != NULL)
+         {
+           if (unlikely (entry->secndx != 0))
+             wr_error ()
+               << "Multiple occurrences of section " << scnname << std::endl;
+           else
+             {
+               /* Haven't seen a section of that name yet.  */
+               cursec->data = elf_getdata (scn, NULL);
+               if (cursec->data == NULL || cursec->data->d_buf == NULL)
+                 /* Don't print out a warning, we'll get to that in
+                    process_file.  */
+                 cursec->data = NULL;
+               entry->secndx = curndx;
+             }
+         }
+       /* Relocation section.  */
+       else if (shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL)
+         {
+           /* Get data of section that this REL(A) section relocates.  */
+           Elf_Scn *relocated_scn = elf_getscn (elf, shdr->sh_info);
+           Elf_Scn *symtab_scn = elf_getscn (elf, shdr->sh_link);
+           if (relocated_scn == NULL || symtab_scn == NULL)
+             goto invalid_elf;
+
+           GElf_Shdr relocated_shdr_mem;
+           GElf_Shdr *relocated_shdr = gelf_getshdr (relocated_scn,
+                                                     &relocated_shdr_mem);
+           if (relocated_shdr == NULL)
+             goto invalid_elf;
+
+           const char *relocated_scnname
+             = elf_strptr (elf, file->ehdr.e_shstrndx,
+                           relocated_shdr->sh_name);
+           if (unlikely (relocated_scnname == NULL))
+             goto invalid_elf;
+
+           secentry *relocated = secinfo.get (relocated_scnname);
+           if (relocated != NULL)
+             {
+               if (relocated->reldata != NULL)
+                 wr_error ()
+                   << "Several relocation sections for debug section "
+                   << relocated_scnname << ". Ignoring " << scnname
+                   << "." << std::endl;
+               else
+                 {
+                   relocated->reldata = elf_getdata (scn, NULL);
+                   if (unlikely (relocated->reldata == NULL
+                                 || relocated->reldata->d_buf == NULL))
+                     {
+                       wr_error ()
+                         << "Data-less relocation section " << scnname
+                         << "." << std::endl;
+                       relocated->reldata = NULL;
+                     }
+                   else
+                     relocated->reltype = shdr->sh_type;
+                 }
+
+               if (reloc_symtab == NULL)
+                 reloc_symtab = symtab_scn;
+               else if (reloc_symtab != symtab_scn)
+                 wr_error ()
+                   << "Relocation sections use multiple symbol tables."
+                   << std::endl;
+             }
+         }
+      }
+
+    for (secinfo_map::iterator it = secinfo.begin (); it != secinfo.end (); ++it)
+      if (it->second.secndx != 0)
+       file->debugsec[it->second.id] = file->sec + it->second.secndx;
+
+    if (true)
+      {
+       Elf_Data *reloc_symdata = NULL;
+       if (reloc_symtab != NULL)
+         {
+           reloc_symdata = elf_getdata (reloc_symtab, NULL);
+           if (reloc_symdata == NULL)
+             /* Not a show stopper, we can check a lot of stuff even
+                without a symbol table.  */
+             wr_error () << "Couldn't obtain symtab data." << std::endl;
+         }
+
+       /* Check relocation sections that we've got.  */
+       for (secinfo_map::iterator it = secinfo.begin (); it != secinfo.end (); ++it)
+         {
+           secentry *cur = &it->second;
+           if (cur->secndx != 0 && cur->reldata != NULL)
+             {
+               struct sec *sec = file->sec + cur->secndx;
+               sec->rel.type = cur->reltype;
+               if (sec->data == NULL)
+                 wr_error (section_locus (sec->id))
+                   << "this data-less section has a relocation section."
+                   << std::endl;
+               else if (read_rel (file, sec, cur->reldata, file->addr_64))
+                 sec->rel.symdata = reloc_symdata;
+             }
+         }
+
+       if (secentry *str = secinfo.get (".debug_str"))
+         if (str->reldata != NULL)
+           wr_message (section_locus (sec_str), mc_impact_2 | mc_elf)
+             << "there's a relocation section associated with this section."
+             << std::endl;
+      }
+
+    return true;
+
+  close_and_out:
+    if (elf != NULL)
+      {
+       elf_errno (); // clear errno
+       elf_end (elf);
+       int err = elf_errno ();
+       if (err != 0)
+         wr_error ()
+           << "error while closing Elf descriptor: "
+           << elf_errmsg (err) << std::endl;
+      }
+    return false;
+  }
+}
+
+load_sections::load_sections (checkstack &stack __attribute__ ((unused)),
+                             dwarflint &lint)
+{
+  if (!elf_file_init (&file, lint.fd ()))
+    throw check_base::failed ();
+}
+
+load_sections::~load_sections ()
+{
+  if (file.ebl != NULL)
+    ebl_closebackend (file.ebl);
+  free (file.sec);
+  elf_end (file.elf);
+}
+
+sec &
+section_base::get_sec_or_throw (section_id secid)
+{
+  if (sec *s = sections->file.debugsec[secid])
+    if (s->data != NULL)
+      return *s;
+
+  throw check_base::failed ();
+}
+
+section_base::section_base (checkstack &stack,
+                           dwarflint &lint, section_id secid)
+  : sections (lint.check (stack, sections))
+  , sect (get_sec_or_throw (secid))
+  , file (sections->file)
+{
+}
diff --git a/dwarflint/sections.hh b/dwarflint/sections.hh
new file mode 100644 (file)
index 0000000..9e99f2f
--- /dev/null
@@ -0,0 +1,72 @@
+/* Low-level section handling.
+   Copyright (C) 2009, 2010, 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_SECTIONS_HH
+#define DWARFLINT_SECTIONS_HH
+
+#include "checks.hh"
+#include "elf_file.hh"
+
+class load_sections
+  : public check<load_sections>
+{
+public:
+  static checkdescriptor const *descriptor ();
+
+  elf_file file;
+  load_sections (checkstack &stack, dwarflint &lint);
+  ~load_sections ();
+};
+
+class section_base
+{
+  load_sections *sections;
+  sec &get_sec_or_throw (section_id secid);
+
+public:
+  static checkdescriptor const *descriptor ();
+
+  sec &sect;
+  elf_file &file;
+  section_base (checkstack &stack,
+               dwarflint &lint, section_id secid);
+
+  relocation_data *reldata () const
+  {
+    return sect.rel.size > 0 ? &sect.rel : NULL;
+  }
+};
+
+template<section_id sec_id>
+class section
+  : public section_base
+  , public check<section<sec_id> >
+{
+public:
+  static checkdescriptor const *descriptor () {
+    static checkdescriptor cd
+      (checkdescriptor::create (section_name[sec_id])
+       .hidden ());
+    return &cd;
+  }
+
+  explicit section (checkstack &stack, dwarflint &lint)
+    : section_base (stack, lint, sec_id)
+  {}
+};
+
+#endif//DWARFLINT_SECTIONS_HH
diff --git a/dwarflint/sections_i.hh b/dwarflint/sections_i.hh
new file mode 100644 (file)
index 0000000..ef5ec21
--- /dev/null
@@ -0,0 +1,2 @@
+#include "section_id.hh"
+template<enum section_id> class section;
diff --git a/dwarflint/tests/DW_AT-later-version.bz2 b/dwarflint/tests/DW_AT-later-version.bz2
new file mode 100644 (file)
index 0000000..2a5690b
Binary files /dev/null and b/dwarflint/tests/DW_AT-later-version.bz2 differ
diff --git a/dwarflint/tests/DW_AT_high_pc-below.bz2 b/dwarflint/tests/DW_AT_high_pc-below.bz2
new file mode 100644 (file)
index 0000000..0221d4e
Binary files /dev/null and b/dwarflint/tests/DW_AT_high_pc-below.bz2 differ
diff --git a/dwarflint/tests/DW_AT_high_pc-relative.bz2 b/dwarflint/tests/DW_AT_high_pc-relative.bz2
new file mode 100644 (file)
index 0000000..d519665
Binary files /dev/null and b/dwarflint/tests/DW_AT_high_pc-relative.bz2 differ
diff --git a/dwarflint/tests/aranges_terminate_early.bz2 b/dwarflint/tests/aranges_terminate_early.bz2
new file mode 100755 (executable)
index 0000000..d61925c
Binary files /dev/null and b/dwarflint/tests/aranges_terminate_early.bz2 differ
diff --git a/dwarflint/tests/check_debug_info_refs-1.bz2 b/dwarflint/tests/check_debug_info_refs-1.bz2
new file mode 100755 (executable)
index 0000000..6fe3048
Binary files /dev/null and b/dwarflint/tests/check_debug_info_refs-1.bz2 differ
diff --git a/dwarflint/tests/check_debug_info_refs-2.bz2 b/dwarflint/tests/check_debug_info_refs-2.bz2
new file mode 100644 (file)
index 0000000..0474929
Binary files /dev/null and b/dwarflint/tests/check_debug_info_refs-2.bz2 differ
diff --git a/dwarflint/tests/check_range_out_of_scope-1.bz2 b/dwarflint/tests/check_range_out_of_scope-1.bz2
new file mode 100755 (executable)
index 0000000..1ef81cb
Binary files /dev/null and b/dwarflint/tests/check_range_out_of_scope-1.bz2 differ
diff --git a/dwarflint/tests/check_self_referential_die.bz2 b/dwarflint/tests/check_self_referential_die.bz2
new file mode 100644 (file)
index 0000000..06a4530
Binary files /dev/null and b/dwarflint/tests/check_self_referential_die.bz2 differ
diff --git a/dwarflint/tests/crc7.ko.debug.bz2 b/dwarflint/tests/crc7.ko.debug.bz2
new file mode 100644 (file)
index 0000000..be66966
Binary files /dev/null and b/dwarflint/tests/crc7.ko.debug.bz2 differ
diff --git a/dwarflint/tests/debug_abbrev-duplicate-attribute.bz2 b/dwarflint/tests/debug_abbrev-duplicate-attribute.bz2
new file mode 100644 (file)
index 0000000..119c399
Binary files /dev/null and b/dwarflint/tests/debug_abbrev-duplicate-attribute.bz2 differ
diff --git a/dwarflint/tests/empty-1.bz2 b/dwarflint/tests/empty-1.bz2
new file mode 100644 (file)
index 0000000..25a7ada
Binary files /dev/null and b/dwarflint/tests/empty-1.bz2 differ
diff --git a/dwarflint/tests/garbage-1.bz2 b/dwarflint/tests/garbage-1.bz2
new file mode 100644 (file)
index 0000000..de1b26d
Binary files /dev/null and b/dwarflint/tests/garbage-1.bz2 differ
diff --git a/dwarflint/tests/garbage-10.bz2 b/dwarflint/tests/garbage-10.bz2
new file mode 100644 (file)
index 0000000..2afab22
Binary files /dev/null and b/dwarflint/tests/garbage-10.bz2 differ
diff --git a/dwarflint/tests/garbage-11.bz2 b/dwarflint/tests/garbage-11.bz2
new file mode 100644 (file)
index 0000000..450e72c
Binary files /dev/null and b/dwarflint/tests/garbage-11.bz2 differ
diff --git a/dwarflint/tests/garbage-12.bz2 b/dwarflint/tests/garbage-12.bz2
new file mode 100644 (file)
index 0000000..a38e928
Binary files /dev/null and b/dwarflint/tests/garbage-12.bz2 differ
diff --git a/dwarflint/tests/garbage-2.bz2 b/dwarflint/tests/garbage-2.bz2
new file mode 100644 (file)
index 0000000..3cd5619
Binary files /dev/null and b/dwarflint/tests/garbage-2.bz2 differ
diff --git a/dwarflint/tests/garbage-3.bz2 b/dwarflint/tests/garbage-3.bz2
new file mode 100644 (file)
index 0000000..ad66a41
Binary files /dev/null and b/dwarflint/tests/garbage-3.bz2 differ
diff --git a/dwarflint/tests/garbage-4.bz2 b/dwarflint/tests/garbage-4.bz2
new file mode 100644 (file)
index 0000000..20e75d8
Binary files /dev/null and b/dwarflint/tests/garbage-4.bz2 differ
diff --git a/dwarflint/tests/garbage-5.bz2 b/dwarflint/tests/garbage-5.bz2
new file mode 100644 (file)
index 0000000..9b0401e
Binary files /dev/null and b/dwarflint/tests/garbage-5.bz2 differ
diff --git a/dwarflint/tests/garbage-6.bz2 b/dwarflint/tests/garbage-6.bz2
new file mode 100644 (file)
index 0000000..6cb8a44
Binary files /dev/null and b/dwarflint/tests/garbage-6.bz2 differ
diff --git a/dwarflint/tests/garbage-7.bz2 b/dwarflint/tests/garbage-7.bz2
new file mode 100644 (file)
index 0000000..41963b6
Binary files /dev/null and b/dwarflint/tests/garbage-7.bz2 differ
diff --git a/dwarflint/tests/garbage-8.bz2 b/dwarflint/tests/garbage-8.bz2
new file mode 100644 (file)
index 0000000..b988983
Binary files /dev/null and b/dwarflint/tests/garbage-8.bz2 differ
diff --git a/dwarflint/tests/garbage-9.bz2 b/dwarflint/tests/garbage-9.bz2
new file mode 100644 (file)
index 0000000..c2e7a8b
Binary files /dev/null and b/dwarflint/tests/garbage-9.bz2 differ
diff --git a/dwarflint/tests/hello.bad-1.bz2 b/dwarflint/tests/hello.bad-1.bz2
new file mode 100644 (file)
index 0000000..52531ff
Binary files /dev/null and b/dwarflint/tests/hello.bad-1.bz2 differ
diff --git a/dwarflint/tests/hello.bad-1.s b/dwarflint/tests/hello.bad-1.s
new file mode 100644 (file)
index 0000000..8679ec1
--- /dev/null
@@ -0,0 +1,246 @@
+       .file   "hello.c"
+# GNU C (GCC) version 4.5.1 20100924 (Red Hat 4.5.1-4) (x86_64-redhat-linux)
+#      compiled by GNU C version 4.5.1 20100924 (Red Hat 4.5.1-4), GMP version 4.3.1, MPFR version 2.4.2, MPC version 0.8.1
+# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+# options passed:  hello.c -mtune=generic -march=x86-64 -g -fverbose-asm
+# options enabled:  -falign-loops -fargument-alias
+# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg -fcommon
+# -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining
+# -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fident
+# -finline-functions-called-once -fira-share-save-slots
+# -fira-share-spill-slots -fivopts -fkeep-static-consts
+# -fleading-underscore -fmath-errno -fmerge-debug-strings
+# -fmove-loop-invariants -fpeephole -freg-struct-return
+# -fsched-critical-path-heuristic -fsched-dep-count-heuristic
+# -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic
+# -fsched-rank-heuristic -fsched-spec -fsched-spec-insn-heuristic
+# -fsched-stalled-insns-dep -fshow-column -fsigned-zeros
+# -fsplit-ivs-in-unroller -ftrapping-math -ftree-cselim -ftree-forwprop
+# -ftree-loop-im -ftree-loop-ivcanon -ftree-loop-optimize
+# -ftree-parallelize-loops= -ftree-phiprop -ftree-pta -ftree-reassoc
+# -ftree-scev-cprop -ftree-slp-vectorize -ftree-vect-loop-version
+# -funit-at-a-time -funwind-tables -fvect-cost-model -fverbose-asm
+# -fzero-initialized-in-bss -m128bit-long-double -m64 -m80387
+# -maccumulate-outgoing-args -malign-stringops -mfancy-math-387
+# -mfp-ret-in-387 -mfused-madd -mglibc -mieee-fp -mmmx -mno-sse4
+# -mpush-args -mred-zone -msse -msse2 -mtls-direct-seg-refs
+
+       .section        .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+       .section        .debug_info,"",@progbits
+.Ldebug_info0:
+       .section        .debug_line,"",@progbits
+.Ldebug_line0:
+       .text
+.Ltext0:
+# Compiler executable checksum: ea394b69293dd698607206e8e43d607e
+
+.globl main
+       .type   main, @function
+main:
+.LFB0:
+       .file 1 "hello.c"
+       # hello.c:3
+       .loc 1 3 0
+       .cfi_startproc
+       # basic block 2
+       pushq   %rbp    #       # 15    *pushdi2_rex64/1        [length = 1]
+       .cfi_def_cfa_offset 16
+       .cfi_offset 6, -16
+       movq    %rsp, %rbp      #,      # 16    *movdi_1_rex64/2        [length = 3]
+       .cfi_def_cfa_register 6
+       movl    %edi, -4(%rbp)  # argc, argc    # 2     *movsi_1/2      [length = 3]
+       movq    %rsi, -16(%rbp) # argv, argv    # 3     *movdi_1_rex64/4        [length = 4]
+       # hello.c:4
+       .loc 1 4 0
+       leave   # 21    leave_rex64     [length = 1]
+       .cfi_def_cfa 7, 8
+       ret     # 22    return_internal [length = 1]
+       .cfi_endproc
+.LFE0:
+       .size   main, .-main
+.Letext0:
+       .section        .debug_info
+       .long   0x84    # Length of Compilation Unit Info
+       .value  0x3     # DWARF version number
+       .long   .Ldebug_abbrev0 # Offset Into Abbrev. Section
+       .byte   0x8     # Pointer Size (in bytes)
+       .uleb128 0x1    # (DIE (0xb) DW_TAG_compile_unit)
+       .long   .LASF3  # DW_AT_producer: "GNU C 4.5.1 20100924 (Red Hat 4.5.1-4)"
+       .byte   0x1     # DW_AT_language
+       .long   .LASF4  # DW_AT_name: "hello.c"
+       .long   .LASF5  # DW_AT_comp_dir: "/home/mark/src/tests"
+       .quad   .Ltext0 # DW_AT_low_pc
+       .quad   .Letext0        # DW_AT_high_pc
+       .long   .Ldebug_line0   # DW_AT_stmt_list
+       .uleb128 0x2    # (DIE (0x2d) DW_TAG_subprogram)
+       .byte   0x1     # DW_AT_external
+       .long   .LASF6  # DW_AT_name: "main"
+       .byte   0x1     # DW_AT_decl_file (hello.c)
+       .byte   0x2     # DW_AT_decl_line
+       .byte   0x1     # DW_AT_prototyped
+       .long   0x6d    # DW_AT_type
+       .quad   .LFB0   # DW_AT_low_pc
+       .quad   .LFE0   # DW_AT_high_pc
+       .byte   0x1     # DW_AT_frame_base
+       .byte   0x9c    # DW_OP_call_frame_cfa
+       .long   0x6d    # DW_AT_sibling
+       .uleb128 0x3    # (DIE (0x50) DW_TAG_formal_parameter)
+       .long   .LASF0  # DW_AT_name: "argc"
+       .byte   0x1     # DW_AT_decl_file (hello.c)
+       .byte   0x2     # DW_AT_decl_line
+       .long   0x6d    # DW_AT_type
+       .byte   0x2     # DW_AT_location
+       .byte   0x91    # DW_OP_fbreg
+       .sleb128 -20
+       .uleb128 0x3    # (DIE (0x5e) DW_TAG_formal_parameter)
+       .long   .LASF1  # DW_AT_name: "argv"
+       .byte   0x1     # DW_AT_decl_file (hello.c)
+       .byte   0x2     # DW_AT_decl_line
+       .long   0x74    # DW_AT_type
+       .byte   0x2     # DW_AT_location
+       .byte   0x91    # DW_OP_fbreg
+       .sleb128 -32
+       .byte   0x0     # end of children of DIE 0x2d
+       .uleb128 0x4    # (DIE (0x6d) DW_TAG_base_type)
+       .byte   0x4     # DW_AT_byte_size
+       .byte   0x5     # DW_AT_encoding
+       .ascii "int\0"  # DW_AT_name
+       .uleb128 0x5    # (DIE (0x74) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0x7a    # DW_AT_type
+       .uleb128 0x5    # (DIE (0x7a) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0x80    # DW_AT_type
+       .uleb128 0x6    # (DIE (0x80) DW_TAG_base_type)
+       .byte   0x1     # DW_AT_byte_size
+       .byte   0x6     # DW_AT_encoding
+       .long   .LASF2  # DW_AT_name: "char"
+       .byte   0x0     # end of children of DIE 0xb
+       .section        .debug_abbrev
+       .uleb128 0x1    # (abbrev code)
+       .uleb128 0x11   # (TAG: DW_TAG_compile_unit)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x25   # (DW_AT_producer)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x13   # (DW_AT_language)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x1b   # (DW_AT_comp_dir)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x10   # (DW_AT_stmt_list)
+       .uleb128 0x6    # (DW_FORM_data4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x2    # (abbrev code)
+       .uleb128 0x2e   # (TAG: DW_TAG_subprogram)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x3f   # (DW_AT_external)
+       .uleb128 0xc    # (DW_FORM_flag)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x27   # (DW_AT_prototyped)
+       .uleb128 0xc    # (DW_FORM_flag)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x40   # (DW_AT_frame_base)
+       .uleb128 0xa    # (DW_FORM_block1)
+       .uleb128 0x1    # (DW_AT_sibling)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x3    # (abbrev code)
+       .uleb128 0x5    # (TAG: DW_TAG_formal_parameter)
+       .byte   0x0     # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x2    # (DW_AT_location)
+       .uleb128 0xa    # (DW_FORM_block1)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x4    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x5    # (abbrev code)
+       .uleb128 0xf    # (TAG: DW_TAG_pointer_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x6    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .byte   0x0
+       .byte   0x0
+       .byte   0x0
+       .section        .debug_pubnames,"",@progbits
+       .long   0x17    # Length of Public Names Info
+       .value  0x2     # DWARF Version
+       .long   .Ldebug_info0   # Offset of Compilation Unit Info
+       .long   0x88    # Compilation Unit Length
+       .long   0x2d    # DIE offset
+       .ascii "main\0" # external name
+       .long   0x0
+       .section        .debug_aranges,"",@progbits
+       .long   0x2c    # Length of Address Ranges Info
+       .value  0x2     # DWARF Version
+       .long   .Ldebug_info0   # Offset of Compilation Unit Info
+       .byte   0x8     # Size of Address
+       .byte   0x0     # Size of Segment Descriptor
+       .value  0x0     # Pad to 16 byte boundary
+       .value  0x0
+       .quad   .Ltext0 # Address
+       .quad   .Letext0-.Ltext0        # Length
+       .quad   0x0
+       .quad   0x0
+       .section        .debug_str,"MS",@progbits,1
+.LASF1:
+       .string "argv"
+.LASF4:
+       .string "hello.c"
+.LASF5:
+       .string "/home/mark/src/tests"
+.LASF6:
+       .string "main"
+.LASF0:
+       .string "argc"
+.LASF3:
+       .string "GNU C 4.5.1 20100924 (Red Hat 4.5.1-4)"
+.LASF2:
+       .string "char"
+       .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"
+       .section        .note.GNU-stack,"",@progbits
diff --git a/dwarflint/tests/hello.bad-2.bz2 b/dwarflint/tests/hello.bad-2.bz2
new file mode 100644 (file)
index 0000000..8ccbe37
Binary files /dev/null and b/dwarflint/tests/hello.bad-2.bz2 differ
diff --git a/dwarflint/tests/hello.bad-2.s b/dwarflint/tests/hello.bad-2.s
new file mode 100644 (file)
index 0000000..5fcd2d5
--- /dev/null
@@ -0,0 +1,246 @@
+       .file   "hello.c"
+# GNU C (GCC) version 4.5.1 20100924 (Red Hat 4.5.1-4) (x86_64-redhat-linux)
+#      compiled by GNU C version 4.5.1 20100924 (Red Hat 4.5.1-4), GMP version 4.3.1, MPFR version 2.4.2, MPC version 0.8.1
+# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+# options passed:  hello.c -mtune=generic -march=x86-64 -g -fverbose-asm
+# options enabled:  -falign-loops -fargument-alias
+# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg -fcommon
+# -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining
+# -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fident
+# -finline-functions-called-once -fira-share-save-slots
+# -fira-share-spill-slots -fivopts -fkeep-static-consts
+# -fleading-underscore -fmath-errno -fmerge-debug-strings
+# -fmove-loop-invariants -fpeephole -freg-struct-return
+# -fsched-critical-path-heuristic -fsched-dep-count-heuristic
+# -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic
+# -fsched-rank-heuristic -fsched-spec -fsched-spec-insn-heuristic
+# -fsched-stalled-insns-dep -fshow-column -fsigned-zeros
+# -fsplit-ivs-in-unroller -ftrapping-math -ftree-cselim -ftree-forwprop
+# -ftree-loop-im -ftree-loop-ivcanon -ftree-loop-optimize
+# -ftree-parallelize-loops= -ftree-phiprop -ftree-pta -ftree-reassoc
+# -ftree-scev-cprop -ftree-slp-vectorize -ftree-vect-loop-version
+# -funit-at-a-time -funwind-tables -fvect-cost-model -fverbose-asm
+# -fzero-initialized-in-bss -m128bit-long-double -m64 -m80387
+# -maccumulate-outgoing-args -malign-stringops -mfancy-math-387
+# -mfp-ret-in-387 -mfused-madd -mglibc -mieee-fp -mmmx -mno-sse4
+# -mpush-args -mred-zone -msse -msse2 -mtls-direct-seg-refs
+
+       .section        .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+       .section        .debug_info,"",@progbits
+.Ldebug_info0:
+       .section        .debug_line,"",@progbits
+.Ldebug_line0:
+       .text
+.Ltext0:
+# Compiler executable checksum: ea394b69293dd698607206e8e43d607e
+
+.globl main
+       .type   main, @function
+main:
+.LFB0:
+       .file 1 "hello.c"
+       # hello.c:3
+       .loc 1 3 0
+       .cfi_startproc
+       # basic block 2
+       pushq   %rbp    #       # 15    *pushdi2_rex64/1        [length = 1]
+       .cfi_def_cfa_offset 16
+       .cfi_offset 6, -16
+       movq    %rsp, %rbp      #,      # 16    *movdi_1_rex64/2        [length = 3]
+       .cfi_def_cfa_register 6
+       movl    %edi, -4(%rbp)  # argc, argc    # 2     *movsi_1/2      [length = 3]
+       movq    %rsi, -16(%rbp) # argv, argv    # 3     *movdi_1_rex64/4        [length = 4]
+       # hello.c:4
+       .loc 1 4 0
+       leave   # 21    leave_rex64     [length = 1]
+       .cfi_def_cfa 7, 8
+       ret     # 22    return_internal [length = 1]
+       .cfi_endproc
+.LFE0:
+       .size   main, .-main
+.Letext0:
+       .section        .debug_info
+       .long   0x84    # Length of Compilation Unit Info
+       .value  0x3     # DWARF version number
+       .long   .Ldebug_abbrev0 # Offset Into Abbrev. Section
+       .byte   0x8     # Pointer Size (in bytes)
+       .uleb128 0x1    # (DIE (0xb) DW_TAG_compile_unit)
+       .long   .LASF3  # DW_AT_producer: "GNU C 4.5.1 20100924 (Red Hat 4.5.1-4)"
+       .byte   0x1     # DW_AT_language
+       .long   .LASF4  # DW_AT_name: "hello.c"
+       .long   .LASF5  # DW_AT_comp_dir: "/home/mark/src/tests"
+       .quad   .Ltext0 # DW_AT_low_pc
+       .quad   .Letext0        # DW_AT_high_pc
+       .long   .Ldebug_line0   # DW_AT_stmt_list
+       .uleb128 0x2    # (DIE (0x2d) DW_TAG_subprogram)
+       .byte   0x1     # DW_AT_external
+       .long   .LASF6  # DW_AT_name: "main"
+       .byte   0x1     # DW_AT_decl_file (hello.c)
+       .byte   0x2     # DW_AT_decl_line
+       .byte   0x1     # DW_AT_prototyped
+       .long   0x6d    # DW_AT_type
+       .quad   .LFB0   # DW_AT_low_pc
+       .quad   .LFE0   # DW_AT_high_pc
+       .byte   0x1     # DW_AT_frame_base
+       .byte   0x9c    # DW_OP_call_frame_cfa
+       .long   0x6d    # DW_AT_sibling
+       .uleb128 0x3    # (DIE (0x50) DW_TAG_formal_parameter)
+       .long   .LASF0  # DW_AT_name: "argc"
+       .byte   0x1     # DW_AT_decl_file (hello.c)
+       .byte   0x2     # DW_AT_decl_line
+       .long   0x6d    # DW_AT_type
+       .byte   0x2     # DW_AT_location
+       .byte   0x91    # DW_OP_fbreg
+       .sleb128 -20
+       .uleb128 0x3    # (DIE (0x5e) DW_TAG_formal_parameter)
+       .long   .LASF1  # DW_AT_name: "argv"
+       .byte   0x1     # DW_AT_decl_file (hello.c)
+       .byte   0x2     # DW_AT_decl_line
+       .long   0x74    # DW_AT_type
+       .byte   0x2     # DW_AT_location
+       .byte   0x91    # DW_OP_fbreg
+       .sleb128 -32
+       .byte   0x0     # end of children of DIE 0x2d
+       .uleb128 0x4    # (DIE (0x6d) DW_TAG_base_type)
+       .byte   0x4     # DW_AT_byte_size
+       .byte   0x5     # DW_AT_encoding
+       .ascii "int\0"  # DW_AT_name
+       .uleb128 0x5    # (DIE (0x74) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0x7a    # DW_AT_type
+       .uleb128 0x5    # (DIE (0x7a) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0x80    # DW_AT_type
+       .uleb128 0x6    # (DIE (0x80) DW_TAG_base_type)
+       .byte   0x1     # DW_AT_byte_size
+       .byte   0x6     # DW_AT_encoding
+       .long   .LASF2  # DW_AT_name: "char"
+       .byte   0x0     # end of children of DIE 0xb
+       .section        .debug_abbrev
+       .uleb128 0x1    # (abbrev code)
+       .uleb128 0x11   # (TAG: DW_TAG_compile_unit)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x25   # (DW_AT_producer)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x13   # (DW_AT_language)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x1b   # (DW_AT_comp_dir)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x2    # (abbrev code)
+       .uleb128 0x2e   # (TAG: DW_TAG_subprogram)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x3f   # (DW_AT_external)
+       .uleb128 0xc    # (DW_FORM_flag)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x27   # (DW_AT_prototyped)
+       .uleb128 0xc    # (DW_FORM_flag)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x40   # (DW_AT_frame_base)
+       .uleb128 0xa    # (DW_FORM_block1)
+       .uleb128 0x1    # (DW_AT_sibling)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x3    # (abbrev code)
+       .uleb128 0x5    # (TAG: DW_TAG_formal_parameter)
+       .byte   0x0     # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x2    # (DW_AT_location)
+       .uleb128 0xa    # (DW_FORM_block1)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x4    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x5    # (abbrev code)
+       .uleb128 0xf    # (TAG: DW_TAG_pointer_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x6    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .byte   0x0
+       .byte   0x0
+       .byte   0x0
+       .section        .debug_pubnames,"",@progbits
+       .long   0x17    # Length of Public Names Info
+       .value  0x2     # DWARF Version
+       .long   .Ldebug_info0   # Offset of Compilation Unit Info
+       .long   0x88    # Compilation Unit Length
+       .long   0x2d    # DIE offset
+       .ascii "main\0" # external name
+       .long   0x0
+       .section        .debug_aranges,"",@progbits
+       .long   0x2c    # Length of Address Ranges Info
+       .value  0x2     # DWARF Version
+       .long   .Ldebug_info0   # Offset of Compilation Unit Info
+       .byte   0x8     # Size of Address
+       .byte   0x0     # Size of Segment Descriptor
+       .value  0x0     # Pad to 16 byte boundary
+       .value  0x0
+       .quad   .Ltext0 # Address
+       .quad   .Letext0-.Ltext0        # Length
+       .quad   0x0
+       .quad   0x0
+       .section        .debug_str,"MS",@progbits,1
+.LASF1:
+       .string "argv"
+.LASF4:
+       .string "hello.c"
+.LASF5:
+       .string "/home/mark/src/tests"
+.LASF6:
+       .string "main"
+.LASF0:
+       .string "argc"
+.LASF3:
+       .string "GNU C 4.5.1 20100924 (Red Hat 4.5.1-4)"
+.LASF2:
+       .string "char"
+       .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"
+       .section        .note.GNU-stack,"",@progbits
diff --git a/dwarflint/tests/hello.bad-3.bz2 b/dwarflint/tests/hello.bad-3.bz2
new file mode 100644 (file)
index 0000000..35b73e7
Binary files /dev/null and b/dwarflint/tests/hello.bad-3.bz2 differ
diff --git a/dwarflint/tests/hello.bad-3.s b/dwarflint/tests/hello.bad-3.s
new file mode 100644 (file)
index 0000000..5f0041b
--- /dev/null
@@ -0,0 +1,313 @@
+       .file   "hello-nested.c"
+# GNU C (GCC) version 4.5.1 20100924 (Red Hat 4.5.1-4) (x86_64-redhat-linux)
+#      compiled by GNU C version 4.5.1 20100924 (Red Hat 4.5.1-4), GMP version 4.3.1, MPFR version 2.4.2, MPC version 0.8.1
+# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+# options passed:  hello-nested.c -mtune=generic -march=x86-64 -g
+# -fverbose-asm
+# options enabled:  -falign-loops -fargument-alias
+# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg -fcommon
+# -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining
+# -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fident
+# -finline-functions-called-once -fira-share-save-slots
+# -fira-share-spill-slots -fivopts -fkeep-static-consts
+# -fleading-underscore -fmath-errno -fmerge-debug-strings
+# -fmove-loop-invariants -fpeephole -freg-struct-return
+# -fsched-critical-path-heuristic -fsched-dep-count-heuristic
+# -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic
+# -fsched-rank-heuristic -fsched-spec -fsched-spec-insn-heuristic
+# -fsched-stalled-insns-dep -fshow-column -fsigned-zeros
+# -fsplit-ivs-in-unroller -ftrapping-math -ftree-cselim -ftree-forwprop
+# -ftree-loop-im -ftree-loop-ivcanon -ftree-loop-optimize
+# -ftree-parallelize-loops= -ftree-phiprop -ftree-pta -ftree-reassoc
+# -ftree-scev-cprop -ftree-slp-vectorize -ftree-vect-loop-version
+# -funit-at-a-time -funwind-tables -fvect-cost-model -fverbose-asm
+# -fzero-initialized-in-bss -m128bit-long-double -m64 -m80387
+# -maccumulate-outgoing-args -malign-stringops -mfancy-math-387
+# -mfp-ret-in-387 -mfused-madd -mglibc -mieee-fp -mmmx -mno-sse4
+# -mpush-args -mred-zone -msse -msse2 -mtls-direct-seg-refs
+
+       .section        .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+       .section        .debug_info,"",@progbits
+.Ldebug_info0:
+       .section        .debug_line,"",@progbits
+.Ldebug_line0:
+       .text
+.Ltext0:
+# Compiler executable checksum: ea394b69293dd698607206e8e43d607e
+
+.globl main
+       .type   main, @function
+main:
+.LFB0:
+       .file 1 "hello-nested.c"
+       # hello-nested.c:3
+       .loc 1 3 0
+       .cfi_startproc
+       # basic block 2
+       pushq   %rbp    #       # 15    *pushdi2_rex64/1        [length = 1]
+       .cfi_def_cfa_offset 16
+       .cfi_offset 6, -16
+       movq    %rsp, %rbp      #,      # 16    *movdi_1_rex64/2        [length = 3]
+       .cfi_def_cfa_register 6
+       movl    %edi, -4(%rbp)  # argc, argc    # 2     *movsi_1/2      [length = 3]
+       movq    %rsi, -16(%rbp) # argv, argv    # 3     *movdi_1_rex64/4        [length = 4]
+       # hello-nested.c:6
+       .loc 1 6 0
+       leave   # 21    leave_rex64     [length = 1]
+       .cfi_def_cfa 7, 8
+       ret     # 22    return_internal [length = 1]
+       .cfi_endproc
+.LFE0:
+       .size   main, .-main
+.Letext0:
+       .section        .debug_info
+       .long   0xae    # Length of Compilation Unit Info
+       .value  0x3     # DWARF version number
+       .long   .Ldebug_abbrev0 # Offset Into Abbrev. Section
+       .byte   0x8     # Pointer Size (in bytes)
+       .uleb128 0x1    # (DIE (0xb) DW_TAG_compile_unit)
+       .long   .LASF3  # DW_AT_producer: "GNU C 4.5.1 20100924 (Red Hat 4.5.1-4)"
+       .byte   0x1     # DW_AT_language
+       .long   .LASF4  # DW_AT_name: "hello-nested.c"
+       .long   .LASF5  # DW_AT_comp_dir: "/home/mark/src/tests"
+       .quad   .Ltext0 # DW_AT_low_pc
+       .quad   .Letext0        # DW_AT_high_pc
+       .long   .Ldebug_line0   # DW_AT_stmt_list
+       .uleb128 0x2    # (DIE (0x2d) DW_TAG_subprogram)
+       .byte   0x1     # DW_AT_external
+       .long   .LASF6  # DW_AT_name: "main"
+       .byte   0x1     # DW_AT_decl_file (hello-nested.c)
+       .byte   0x2     # DW_AT_decl_line
+       .byte   0x1     # DW_AT_prototyped
+       .long   0x91    # DW_AT_type
+       .quad   .LFB0   # DW_AT_low_pc
+       .quad   .LFE0   # DW_AT_high_pc
+       .byte   0x1     # DW_AT_frame_base
+       .byte   0x9c    # DW_OP_call_frame_cfa
+       .long   0x91    # DW_AT_sibling
+       .uleb128 0x3    # (DIE (0x50) DW_TAG_formal_parameter)
+       .long   .LASF0  # DW_AT_name: "argc"
+       .byte   0x1     # DW_AT_decl_file (hello-nested.c)
+       .byte   0x2     # DW_AT_decl_line
+       .long   0x91    # DW_AT_type
+       .byte   0x2     # DW_AT_location
+       .byte   0x91    # DW_OP_fbreg
+       .sleb128 -20
+       .uleb128 0x3    # (DIE (0x5e) DW_TAG_formal_parameter)
+       .long   .LASF1  # DW_AT_name: "argv"
+       .byte   0x1     # DW_AT_decl_file (hello-nested.c)
+       .byte   0x2     # DW_AT_decl_line
+       .long   0x98    # DW_AT_type
+       .byte   0x2     # DW_AT_location
+       .byte   0x91    # DW_OP_fbreg
+       .sleb128 -32
+       .uleb128 0x4    # (DIE (0x6c) DW_TAG_structure_type)
+       .ascii "foo\0"  # DW_AT_name
+       .byte   0x4     # DW_AT_byte_size
+       .byte   0x1     # DW_AT_decl_file (hello-nested.c)
+       .byte   0x4     # DW_AT_decl_line
+       .long   0x85    # DW_AT_sibling
+       .uleb128 0x5    # (DIE (0x78) DW_TAG_member)
+       .ascii "bar\0"  # DW_AT_name
+       .byte   0x1     # DW_AT_decl_file (hello-nested.c)
+       .byte   0x4     # DW_AT_decl_line
+       .long   0x91    # DW_AT_type
+       .sleb128 0      # DW_AT_data_member_location
+       .byte   0x0     # end of children of DIE 0x6c
+       .uleb128 0x6    # (DIE (0x85) DW_TAG_variable)
+       .ascii "baz\0"  # DW_AT_name
+       .byte   0x1     # DW_AT_decl_file (hello-nested.c)
+       .byte   0x5     # DW_AT_decl_line
+       .long   0xab    # DW_AT_type
+       .byte   0x0     # end of children of DIE 0x2d
+       .uleb128 0x7    # (DIE (0x91) DW_TAG_base_type)
+       .byte   0x4     # DW_AT_byte_size
+       .byte   0x5     # DW_AT_encoding
+       .ascii "int\0"  # DW_AT_name
+       .uleb128 0x8    # (DIE (0x98) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0x9e    # DW_AT_type
+       .uleb128 0x8    # (DIE (0x9e) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0xa4    # DW_AT_type
+       .uleb128 0x9    # (DIE (0xa4) DW_TAG_base_type)
+       .byte   0x1     # DW_AT_byte_size
+       .byte   0x6     # DW_AT_encoding
+       .long   .LASF2  # DW_AT_name: "char"
+       .uleb128 0x8    # (DIE (0xab) DW_TAG_pointer_type)
+       .byte   0x8     # DW_AT_byte_size
+       .long   0x6c    # DW_AT_type
+       .byte   0x0     # end of children of DIE 0xb
+       .section        .debug_abbrev
+       .uleb128 0x1    # (abbrev code)
+       .uleb128 0x11   # (TAG: DW_TAG_compile_unit)
+       .byte   0x1     # DW_children_yes
+       .uleb128 0x25   # (DW_AT_producer)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x13   # (DW_AT_language)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x1b   # (DW_AT_comp_dir)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x10   # (DW_AT_stmt_list)
+       .uleb128 0x6    # (DW_FORM_data4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x2    # (abbrev code)
+       .uleb128 0x2e   # (TAG: DW_TAG_subprogram)
+       .byte   0x1     # DW_children_no
+       .uleb128 0x3f   # (DW_AT_external)
+       .uleb128 0xc    # (DW_FORM_flag)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x27   # (DW_AT_prototyped)
+       .uleb128 0xc    # (DW_FORM_flag)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x11   # (DW_AT_low_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x12   # (DW_AT_high_pc)
+       .uleb128 0x1    # (DW_FORM_addr)
+       .uleb128 0x40   # (DW_AT_frame_base)
+       .uleb128 0xa    # (DW_FORM_block1)
+       .uleb128 0x54   # (DW_AT_extension)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x3    # (abbrev code)
+       .uleb128 0x5    # (TAG: DW_TAG_formal_parameter)
+       .byte   0x0     # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x2    # (DW_AT_location)
+       .uleb128 0xa    # (DW_FORM_block1)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x4    # (abbrev code)
+       .uleb128 0x13   # (TAG: DW_TAG_structure_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x54   # (DW_AT_extension)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x5    # (abbrev code)
+       .uleb128 0xd    # (TAG: DW_TAG_member)
+       .byte   0x0     # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .uleb128 0x38   # (DW_AT_data_member_location)
+       .uleb128 0xd    # (DW_FORM_sdata)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x6    # (abbrev code)
+       .uleb128 0x34   # (TAG: DW_TAG_variable)
+       .byte   0x0     # DW_children_no
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .uleb128 0x3a   # (DW_AT_decl_file)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3b   # (DW_AT_decl_line)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x7    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0x8    # (DW_FORM_string)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x8    # (abbrev code)
+       .uleb128 0xf    # (TAG: DW_TAG_pointer_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x49   # (DW_AT_type)
+       .uleb128 0x13   # (DW_FORM_ref4)
+       .byte   0x0
+       .byte   0x0
+       .uleb128 0x9    # (abbrev code)
+       .uleb128 0x24   # (TAG: DW_TAG_base_type)
+       .byte   0x0     # DW_children_no
+       .uleb128 0xb    # (DW_AT_byte_size)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3e   # (DW_AT_encoding)
+       .uleb128 0xb    # (DW_FORM_data1)
+       .uleb128 0x3    # (DW_AT_name)
+       .uleb128 0xe    # (DW_FORM_strp)
+       .byte   0x0
+       .byte   0x0
+       .byte   0x0
+       .section        .debug_pubnames,"",@progbits
+       .long   0x17    # Length of Public Names Info
+       .value  0x2     # DWARF Version
+       .long   .Ldebug_info0   # Offset of Compilation Unit Info
+       .long   0xb2    # Compilation Unit Length
+       .long   0x2d    # DIE offset
+       .ascii "main\0" # external name
+       .long   0x0
+       .section        .debug_aranges,"",@progbits
+       .long   0x2c    # Length of Address Ranges Info
+       .value  0x2     # DWARF Version
+       .long   .Ldebug_info0   # Offset of Compilation Unit Info
+       .byte   0x8     # Size of Address
+       .byte   0x0     # Size of Segment Descriptor
+       .value  0x0     # Pad to 16 byte boundary
+       .value  0x0
+       .quad   .Ltext0 # Address
+       .quad   .Letext0-.Ltext0        # Length
+       .quad   0x0
+       .quad   0x0
+       .section        .debug_str,"MS",@progbits,1
+.LASF5:
+       .string "/home/mark/src/tests"
+.LASF3:
+       .string "GNU C 4.5.1 20100924 (Red Hat 4.5.1-4)"
+.LASF0:
+       .string "argc"
+.LASF2:
+       .string "char"
+.LASF6:
+       .string "main"
+.LASF4:
+       .string "hello-nested.c"
+.LASF1:
+       .string "argv"
+       .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"
+       .section        .note.GNU-stack,"",@progbits
diff --git a/dwarflint/tests/libdl-2.12.so.debug.bz2 b/dwarflint/tests/libdl-2.12.so.debug.bz2
new file mode 100644 (file)
index 0000000..356520e
Binary files /dev/null and b/dwarflint/tests/libdl-2.12.so.debug.bz2 differ
diff --git a/dwarflint/tests/location-leaks.bz2 b/dwarflint/tests/location-leaks.bz2
new file mode 100644 (file)
index 0000000..ccf6532
Binary files /dev/null and b/dwarflint/tests/location-leaks.bz2 differ
diff --git a/dwarflint/tests/nodebug.bz2 b/dwarflint/tests/nodebug.bz2
new file mode 100755 (executable)
index 0000000..22320a4
Binary files /dev/null and b/dwarflint/tests/nodebug.bz2 differ
diff --git a/dwarflint/tests/null.o.bz2 b/dwarflint/tests/null.o.bz2
new file mode 100644 (file)
index 0000000..f272d5a
Binary files /dev/null and b/dwarflint/tests/null.o.bz2 differ
diff --git a/dwarflint/tests/run-DW_AT-later-version.sh b/dwarflint/tests/run-DW_AT-later-version.sh
new file mode 100755 (executable)
index 0000000..d662502
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles DW_AT-later-version
+
+testrun_compare ./dwarflint --nognu DW_AT-later-version <<EOF
+warning: .debug_abbrev: abbr. 0x11, attr. endianity: attribute from later DWARF version.
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: DIE 0x29: variable has decl_file, but NOT decl_line
+EOF
diff --git a/dwarflint/tests/run-DW_AT_high_pc-below.sh b/dwarflint/tests/run-DW_AT_high_pc-below.sh
new file mode 100755 (executable)
index 0000000..4b4d6ea
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+# Hand-crafted file that has 0,0 pair in aranges presented before the
+# actual end of the table.
+testfiles DW_AT_high_pc-below
+
+testrun_compare ./dwarflint --check=@low DW_AT_high_pc-below <<EOF
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+EOF
diff --git a/dwarflint/tests/run-DW_AT_high_pc-relative.sh b/dwarflint/tests/run-DW_AT_high_pc-relative.sh
new file mode 100755 (executable)
index 0000000..8757ba0
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+# Hand-crafted file that has 0,0 pair in aranges presented before the
+# actual end of the table.
+testfiles DW_AT_high_pc-relative
+
+testrun_compare ./dwarflint --check=@low DW_AT_high_pc-relative <<EOF
+No errors
+EOF
diff --git a/dwarflint/tests/run-aranges_terminate_early.sh b/dwarflint/tests/run-aranges_terminate_early.sh
new file mode 100755 (executable)
index 0000000..43b2ec2
--- /dev/null
@@ -0,0 +1,34 @@
+#! /bin/sh
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+# Hand-crafted file that has 0,0 pair in aranges presented before the
+# actual end of the table.
+testfiles aranges_terminate_early
+
+testrun_compare ./dwarflint --strict aranges_terminate_early <<EOF
+warning: .debug_aranges: [0x20, 0x30): unnecessary padding with zero bytes.
+warning: .debug_aranges: addresses [0x400474, 0x400481) are covered with CU DIEs, but not with aranges.
+EOF
+
+testrun_compare ./dwarflint --check=check_debug_aranges --strict aranges_terminate_early <<EOF
+warning: .debug_aranges: [0x20, 0x30): unnecessary padding with zero bytes.
+warning: .debug_aranges: addresses [0x400474, 0x400481) are covered with CU DIEs, but not with aranges.
+EOF
diff --git a/dwarflint/tests/run-bad.sh b/dwarflint/tests/run-bad.sh
new file mode 100755 (executable)
index 0000000..ebe3873
--- /dev/null
@@ -0,0 +1,126 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles hello.bad-1 hello.bad-3 empty-1 \
+    garbage-1 garbage-2 garbage-3 garbage-4 \
+    garbage-5 garbage-6 garbage-7 garbage-8 \
+    garbage-9 garbage-10 garbage-11 garbage-12
+
+testrun_compare ./dwarflint hello.bad-1 <<EOF
+error: .debug_info: DIE 0x83: abbrev section at 0x0 doesn't contain code 83.
+EOF
+
+testrun_compare ./dwarflint --check=@low hello.bad-3 <<EOF
+error: .debug_info: DIE 0x2d: This DIE had children, but no DW_AT_sibling attribute.
+error: .debug_info: DIE 0xb: This DIE had children, but no DW_AT_sibling attribute.
+error: .debug_info: DIE 0x91: toplevel DIE chain contains more than one DIE.
+error: .debug_info: DIE 0x98: toplevel DIE chain contains more than one DIE.
+error: .debug_info: DIE 0x9e: toplevel DIE chain contains more than one DIE.
+error: .debug_info: DIE 0xa4: toplevel DIE chain contains more than one DIE.
+error: .debug_info: DIE 0xab: toplevel DIE chain contains more than one DIE.
+EOF
+
+testrun_compare ./dwarflint empty-1 <<EOF
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_line: table 0: no CU uses this line table.
+error: .debug_info: DIE 0x29, attr. decl_file: references .debug_line table, but CU DIE lacks DW_AT_stmt_list.
+EOF
+
+testrun_compare ./dwarflint garbage-1 <<EOF
+error: Broken ELF: offset out of range.
+error: .debug_abbrev: data not found.
+error: .debug_info: data not found.
+EOF
+
+testrun_compare ./dwarflint garbage-2 <<EOF
+error: .debug_info: CU 0: toplevel DIE must be either compile_unit or partial_unit.
+error: .debug_info: DIE 0xab: DIE chain not terminated with null entry.
+EOF
+
+testrun_compare ./dwarflint --check=@low garbage-3 <<EOF
+error: .debug_abbrev: abbr. attribute 0xc: invalid attribute code 0.
+EOF
+
+testrun_compare ./dwarflint garbage-4 <<EOF
+error: .debug_info: DIE 0x6c: this DIE claims that its sibling is 0x80000085 but it's actually 0x85.
+error: .debug_info: DIE 0xab: DIE chain not terminated with null entry.
+EOF
+
+testrun_compare ./dwarflint garbage-5 <<EOF
+error: .debug_info: DIE 0xab: DIE chain not terminated with null entry.
+error: .debug_line: offset 0x3e: not enough data to read an opcode of length 5.
+error: .debug_info: DIE 0xb, attr. stmt_list: unresolved reference to .debug_line table 0x0.
+EOF
+
+testrun_compare ./dwarflint garbage-6 <<EOF
+error: .debug_info: CU 0: invalid address size: 9 (only 4 or 8 allowed).
+error: .debug_info: couldn't load CU headers for processing .debug_abbrev; assuming latest DWARF flavor.
+error: .debug_abbrev: abbr. 0x0, attr. stmt_list: attribute with invalid form DW_FORM_data4.
+error: .debug_abbrev: abbr. 0x13, attr. frame_base: attribute with invalid form DW_FORM_block1.
+error: .debug_abbrev: abbr. 0x2c, attr. location: attribute with invalid form DW_FORM_block1.
+EOF
+
+testrun_compare ./dwarflint garbage-7 <<EOF
+warning: .debug_abbrev: abbr. attribute 0x7e: invalid or unknown name 0x703.
+error: .debug_abbrev: abbr. 0x7a, attr. 0x703: invalid form 0x0.
+error: .debug_abbrev: missing zero to mark end-of-table.
+EOF
+
+testrun_compare ./dwarflint garbage-8 <<EOF
+error: .debug_info: DIE 0x6c, attr. sibling: has a value of 0.
+error: .debug_info: DIE 0x6c: This DIE had children, but no DW_AT_sibling attribute.
+error: .debug_info: DIE 0xab: DIE chain not terminated with null entry.
+EOF
+
+testrun_compare ./dwarflint garbage-9 <<EOF
+error: .debug_info: DIE 0x84, attr. type: invalid reference outside the CU: 0xef00ab.
+error: .debug_info: DIE 0x6c: is the last sibling in chain, but has a DW_AT_sibling attribute.
+error: .debug_info: DIE 0xab: DIE chain not terminated with null entry.
+EOF
+
+testrun_compare ./dwarflint garbage-10 <<EOF
+warning: .rela 0xc of .debug_info: DIE 0xb, attr. producer: relocation formed using STT_SECTION symbol with non-zero value.
+error: .rela 0x11 of .debug_info: DIE 0xb, attr. comp_dir: couldn't obtain symbol #7208969: invalid section index.
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+EOF
+
+testrun_compare ./dwarflint garbage-11 <<EOF
+error: .rela 0x600 of .debug_info: invalid relocation 2560 (<INVALID RELOC>).
+error: .rela 0xc00 of .debug_info: invalid relocation 2560 (<INVALID RELOC>).
+error: .rela 0x1100 of .debug_info: invalid relocation 2560 (<INVALID RELOC>).
+error: .rela 0x1500 of .debug_info: invalid relocation 256 (<INVALID RELOC>).
+error: .rela 0x1d00 of .debug_info: invalid relocation 256 (<INVALID RELOC>).
+error: .rela 0x2500 of .debug_info: invalid relocation 2560 (<INVALID RELOC>).
+error: .rela 0x3600 of .debug_info: invalid relocation 256 (<INVALID RELOC>).
+warning: .debug_info: CU 0: abbrev table offset seems to lack a relocation
+warning: .debug_info: DIE 0xb, attr. producer: strp seems to lack a relocation
+warning: .debug_info: DIE 0xb, attr. comp_dir: strp seems to lack a relocation
+warning: .debug_info: DIE 0xb, attr. stmt_list: data4 seems to lack a relocation
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+error: .debug_line: table 0: header claims that it has a size of 542, but in fact it has a size of 30.
+error: .debug_info: DIE 0xb, attr. stmt_list: unresolved reference to .debug_line table 0x0.
+EOF
+
+testrun_compare ./dwarflint garbage-12 <<EOF
+error: Broken ELF: invalid section header.
+error: .debug_abbrev: data not found.
+error: .debug_info: data not found.
+EOF
diff --git a/dwarflint/tests/run-check_debug_info_refs.sh b/dwarflint/tests/run-check_debug_info_refs.sh
new file mode 100755 (executable)
index 0000000..4e790b8
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/sh
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles check_debug_info_refs-{1,2}
+
+testrun_compare ./dwarflint --check=check_debug_info_refs check_debug_info_refs-1 <<EOF
+error: .debug_aranges: table 48 (CU DIE 95): there has already been arange section for this CU.
+warning: .debug_info: CU 0: no aranges table is associated with this CU.
+EOF
+
+testrun_compare ./dwarflint --check=check_debug_info_refs check_debug_info_refs-1 <<EOF
+error: .debug_aranges: table 48 (CU DIE 95): there has already been arange section for this CU.
+warning: .debug_info: CU 0: no aranges table is associated with this CU.
+EOF
+
+testrun_compare ./dwarflint --check=check_debug_info_refs check_debug_info_refs-2 <<EOF
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: CU 0: no aranges table is associated with this CU.
+EOF
diff --git a/dwarflint/tests/run-check_duplicate_DW_tag_variable.sh b/dwarflint/tests/run-check_duplicate_DW_tag_variable.sh
new file mode 100755 (executable)
index 0000000..c562287
--- /dev/null
@@ -0,0 +1,166 @@
+#! /bin/sh
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles crc7.ko.debug
+
+testrun_compare ./dwarflint --dups=0 --check check_duplicate_DW_tag_variable crc7.ko.debug <<EOF
+warning: .debug_info: DIE 0x40f1: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: CU 16614: no aranges table is associated with this CU.
+warning: .debug_info: DIE 0x3d21: Redeclaration of variable 'console_printk', originally seen at DIE 37f3.
+warning: .debug_info: DIE 0x3d2e: Redeclaration of variable 'hex_asc', originally seen at DIE 380b.
+warning: .debug_info: DIE 0x3d41: Redeclaration of variable '__per_cpu_offset', originally seen at DIE 382e.
+warning: .debug_info: DIE 0x3d4e: Redeclaration of variable 'per_cpu__current_task', originally seen at DIE 383b.
+warning: .debug_info: DIE 0x3d5b: Redeclaration of variable 'pv_info', originally seen at DIE 3848.
+warning: .debug_info: DIE 0x3d69: Redeclaration of variable 'pv_time_ops', originally seen at DIE 3856.
+warning: .debug_info: DIE 0x3d77: Redeclaration of variable 'pv_cpu_ops', originally seen at DIE 3864.
+warning: .debug_info: DIE 0x3d85: Redeclaration of variable 'pv_irq_ops', originally seen at DIE 3872.
+warning: .debug_info: DIE 0x3d93: Redeclaration of variable 'pv_apic_ops', originally seen at DIE 3880.
+warning: .debug_info: DIE 0x3da1: Redeclaration of variable 'pv_mmu_ops', originally seen at DIE 388e.
+warning: .debug_info: DIE 0x3daf: Redeclaration of variable 'nr_cpu_ids', originally seen at DIE 389c.
+warning: .debug_info: DIE 0x3dbc: Redeclaration of variable 'cpu_online_mask', originally seen at DIE 38a9.
+warning: .debug_info: DIE 0x3dc9: Redeclaration of variable 'cpu_present_mask', originally seen at DIE 38bb.
+warning: .debug_info: DIE 0x3dd6: Redeclaration of variable 'cpu_bit_bitmap', originally seen at DIE 38de.
+warning: .debug_info: DIE 0x3de9: Redeclaration of variable 'cpu_callout_mask', originally seen at DIE 38fc.
+warning: .debug_info: DIE 0x3df6: Redeclaration of variable 'boot_cpu_data', originally seen at DIE 3909.
+warning: .debug_info: DIE 0x3e03: Redeclaration of variable 'per_cpu__cpu_info', originally seen at DIE 3916.
+warning: .debug_info: DIE 0x3e10: Redeclaration of variable 'per_cpu__irq_stack_union', originally seen at DIE 3923.
+warning: .debug_info: DIE 0x3e1e: Redeclaration of variable 'mmu_cr4_features', originally seen at DIE 3931.
+warning: .debug_info: DIE 0x3e2c: Redeclaration of variable 'per_cpu__kernel_stack', originally seen at DIE 393f.
+warning: .debug_info: DIE 0x3e39: Redeclaration of variable 'node_states', originally seen at DIE 395c.
+warning: .debug_info: DIE 0x3e47: Redeclaration of variable 'nr_online_nodes', originally seen at DIE 396a.
+warning: .debug_info: DIE 0x3e55: Redeclaration of variable 'page_group_by_mobility_disabled', originally seen at DIE 3978.
+warning: .debug_info: DIE 0x3e6d: Redeclaration of variable 'node_data', originally seen at DIE 3996.
+warning: .debug_info: DIE 0x3e7a: Redeclaration of variable 'ioport_resource', originally seen at DIE 39a3.
+warning: .debug_info: DIE 0x3e87: Redeclaration of variable 'x86_init', originally seen at DIE 39b0.
+warning: .debug_info: DIE 0x3e94: Redeclaration of variable 'smp_found_config', originally seen at DIE 39bd.
+warning: .debug_info: DIE 0x3ea1: Redeclaration of variable 'phys_cpu_present_map', originally seen at DIE 39ca.
+warning: .debug_info: DIE 0x3eae: Redeclaration of variable 'time_status', originally seen at DIE 39d7.
+warning: .debug_info: DIE 0x3ebb: Redeclaration of variable 'jiffies', originally seen at DIE 39e4.
+warning: .debug_info: DIE 0x3ec8: Redeclaration of variable 'timer_stats_active', originally seen at DIE 39f6.
+warning: .debug_info: DIE 0x3ed5: Redeclaration of variable 'acpi_noirq', originally seen at DIE 3a03.
+warning: .debug_info: DIE 0x3ee2: Redeclaration of variable 'acpi_disabled', originally seen at DIE 3a10.
+warning: .debug_info: DIE 0x3eef: Redeclaration of variable 'acpi_ht', originally seen at DIE 3a1d.
+warning: .debug_info: DIE 0x3efc: Redeclaration of variable 'acpi_pci_disabled', originally seen at DIE 3a2a.
+warning: .debug_info: DIE 0x3f09: Redeclaration of variable 'apic_verbosity', originally seen at DIE 3a37.
+warning: .debug_info: DIE 0x3f16: Redeclaration of variable 'disable_apic', originally seen at DIE 3a44.
+warning: .debug_info: DIE 0x3f23: Redeclaration of variable 'x2apic_phys', originally seen at DIE 3a51.
+warning: .debug_info: DIE 0x3f30: Redeclaration of variable 'apic', originally seen at DIE 3a5e.
+warning: .debug_info: DIE 0x3f3e: Redeclaration of variable 'per_cpu__x86_bios_cpu_apicid', originally seen at DIE 3a72.
+warning: .debug_info: DIE 0x3f4b: Redeclaration of variable 'per_cpu__cpu_sibling_map', originally seen at DIE 3a80.
+warning: .debug_info: DIE 0x3f58: Redeclaration of variable 'per_cpu__cpu_core_map', originally seen at DIE 3a8d.
+warning: .debug_info: DIE 0x3f65: Redeclaration of variable 'per_cpu__cpu_number', originally seen at DIE 3a9a.
+warning: .debug_info: DIE 0x3f72: Redeclaration of variable 'smp_ops', originally seen at DIE 3aa7.
+warning: .debug_info: DIE 0x3f7f: Redeclaration of variable 'memnode', originally seen at DIE 3ab4.
+warning: .debug_info: DIE 0x3f8c: Redeclaration of variable 'mem_section', originally seen at DIE 3ad8.
+warning: .debug_info: DIE 0x3f9a: Redeclaration of variable 'per_cpu__x86_cpu_to_node_map', originally seen at DIE 3ae6.
+warning: .debug_info: DIE 0x3fa7: Redeclaration of variable 'x86_cpu_to_node_map_early_ptr', originally seen at DIE 3af3.
+warning: .debug_info: DIE 0x3fb4: Redeclaration of variable 'per_cpu__node_number', originally seen at DIE 3b00.
+warning: .debug_info: DIE 0x3fc1: Redeclaration of variable 'node_to_cpumask_map', originally seen at DIE 3b24.
+warning: .debug_info: DIE 0x3fce: Redeclaration of variable 'gfp_allowed_mask', originally seen at DIE 3b31.
+warning: .debug_info: DIE 0x3fdc: Redeclaration of variable '__tracepoint_kmalloc', originally seen at DIE 3b3f.
+warning: .debug_info: DIE 0x3fe9: Redeclaration of variable '__tracepoint_kmem_cache_alloc', originally seen at DIE 3b4c.
+warning: .debug_info: DIE 0x3ff6: Redeclaration of variable '__tracepoint_kmalloc_node', originally seen at DIE 3b59.
+warning: .debug_info: DIE 0x4003: Redeclaration of variable '__tracepoint_kmem_cache_alloc_node', originally seen at DIE 3b66.
+warning: .debug_info: DIE 0x4010: Redeclaration of variable '__tracepoint_kfree', originally seen at DIE 3b73.
+warning: .debug_info: DIE 0x401d: Redeclaration of variable '__tracepoint_kmem_cache_free', originally seen at DIE 3b80.
+warning: .debug_info: DIE 0x402a: Redeclaration of variable '__tracepoint_mm_page_free_direct', originally seen at DIE 3b8d.
+warning: .debug_info: DIE 0x4037: Redeclaration of variable '__tracepoint_mm_pagevec_free', originally seen at DIE 3b9a.
+warning: .debug_info: DIE 0x4044: Redeclaration of variable '__tracepoint_mm_page_alloc', originally seen at DIE 3ba7.
+warning: .debug_info: DIE 0x4052: Redeclaration of variable '__tracepoint_mm_page_alloc_zone_locked', originally seen at DIE 3bb5.
+warning: .debug_info: DIE 0x4060: Redeclaration of variable '__tracepoint_mm_page_pcpu_drain', originally seen at DIE 3bc3.
+warning: .debug_info: DIE 0x406e: Redeclaration of variable '__tracepoint_mm_page_alloc_extfrag', originally seen at DIE 3bd1.
+warning: .debug_info: DIE 0x407c: Redeclaration of variable 'kmalloc_caches', originally seen at DIE 3bef.
+warning: .debug_info: DIE 0x4089: Redeclaration of variable '__tracepoint_module_load', originally seen at DIE 3bfc.
+warning: .debug_info: DIE 0x4096: Redeclaration of variable '__tracepoint_module_free', originally seen at DIE 3c09.
+warning: .debug_info: DIE 0x40a3: Redeclaration of variable '__tracepoint_module_get', originally seen at DIE 3c16.
+warning: .debug_info: DIE 0x40b0: Redeclaration of variable '__tracepoint_module_put', originally seen at DIE 3c23.
+warning: .debug_info: DIE 0x40bd: Redeclaration of variable '__tracepoint_module_request', originally seen at DIE 3c30.
+warning: .debug_info: DIE 0x40ca: Found definition of variable 'crc7_syndrome_table' whose declaration was seen at DIE 3c4d.
+warning: .debug_info: DIE 0x7cf9: Redeclaration of variable 'console_printk', originally seen at DIE 7831.
+warning: .debug_info: DIE 0x7d06: Redeclaration of variable 'hex_asc', originally seen at DIE 7849.
+warning: .debug_info: DIE 0x7d19: Redeclaration of variable '__per_cpu_offset', originally seen at DIE 786c.
+warning: .debug_info: DIE 0x7d26: Redeclaration of variable 'per_cpu__current_task', originally seen at DIE 7879.
+warning: .debug_info: DIE 0x7d33: Redeclaration of variable 'pv_info', originally seen at DIE 7886.
+warning: .debug_info: DIE 0x7d41: Redeclaration of variable 'pv_time_ops', originally seen at DIE 7894.
+warning: .debug_info: DIE 0x7d4f: Redeclaration of variable 'pv_cpu_ops', originally seen at DIE 78a2.
+warning: .debug_info: DIE 0x7d5d: Redeclaration of variable 'pv_irq_ops', originally seen at DIE 78b0.
+warning: .debug_info: DIE 0x7d6b: Redeclaration of variable 'pv_apic_ops', originally seen at DIE 78be.
+warning: .debug_info: DIE 0x7d79: Redeclaration of variable 'pv_mmu_ops', originally seen at DIE 78cc.
+warning: .debug_info: DIE 0x7d87: Redeclaration of variable 'nr_cpu_ids', originally seen at DIE 78da.
+warning: .debug_info: DIE 0x7d94: Redeclaration of variable 'cpu_online_mask', originally seen at DIE 78e7.
+warning: .debug_info: DIE 0x7da1: Redeclaration of variable 'cpu_present_mask', originally seen at DIE 78f9.
+warning: .debug_info: DIE 0x7dae: Redeclaration of variable 'cpu_bit_bitmap', originally seen at DIE 791c.
+warning: .debug_info: DIE 0x7dc1: Redeclaration of variable 'cpu_callout_mask', originally seen at DIE 793a.
+warning: .debug_info: DIE 0x7dce: Redeclaration of variable 'boot_cpu_data', originally seen at DIE 7947.
+warning: .debug_info: DIE 0x7ddb: Redeclaration of variable 'per_cpu__cpu_info', originally seen at DIE 7954.
+warning: .debug_info: DIE 0x7de8: Redeclaration of variable 'per_cpu__irq_stack_union', originally seen at DIE 7961.
+warning: .debug_info: DIE 0x7df6: Redeclaration of variable 'mmu_cr4_features', originally seen at DIE 796f.
+warning: .debug_info: DIE 0x7e04: Redeclaration of variable 'per_cpu__kernel_stack', originally seen at DIE 797d.
+warning: .debug_info: DIE 0x7e11: Redeclaration of variable 'node_states', originally seen at DIE 799a.
+warning: .debug_info: DIE 0x7e1f: Redeclaration of variable 'nr_online_nodes', originally seen at DIE 79a8.
+warning: .debug_info: DIE 0x7e2d: Redeclaration of variable 'page_group_by_mobility_disabled', originally seen at DIE 79b6.
+warning: .debug_info: DIE 0x7e45: Redeclaration of variable 'node_data', originally seen at DIE 79d4.
+warning: .debug_info: DIE 0x7e52: Redeclaration of variable 'ioport_resource', originally seen at DIE 79e1.
+warning: .debug_info: DIE 0x7e5f: Redeclaration of variable 'x86_init', originally seen at DIE 79ee.
+warning: .debug_info: DIE 0x7e6c: Redeclaration of variable 'smp_found_config', originally seen at DIE 79fb.
+warning: .debug_info: DIE 0x7e79: Redeclaration of variable 'phys_cpu_present_map', originally seen at DIE 7a08.
+warning: .debug_info: DIE 0x7e86: Redeclaration of variable 'time_status', originally seen at DIE 7a15.
+warning: .debug_info: DIE 0x7e93: Redeclaration of variable 'jiffies', originally seen at DIE 7a22.
+warning: .debug_info: DIE 0x7ea0: Redeclaration of variable 'timer_stats_active', originally seen at DIE 7a34.
+warning: .debug_info: DIE 0x7ead: Redeclaration of variable 'acpi_noirq', originally seen at DIE 7a41.
+warning: .debug_info: DIE 0x7eba: Redeclaration of variable 'acpi_disabled', originally seen at DIE 7a4e.
+warning: .debug_info: DIE 0x7ec7: Redeclaration of variable 'acpi_ht', originally seen at DIE 7a5b.
+warning: .debug_info: DIE 0x7ed4: Redeclaration of variable 'acpi_pci_disabled', originally seen at DIE 7a68.
+warning: .debug_info: DIE 0x7ee1: Redeclaration of variable 'apic_verbosity', originally seen at DIE 7a75.
+warning: .debug_info: DIE 0x7eee: Redeclaration of variable 'disable_apic', originally seen at DIE 7a82.
+warning: .debug_info: DIE 0x7efb: Redeclaration of variable 'x2apic_phys', originally seen at DIE 7a8f.
+warning: .debug_info: DIE 0x7f08: Redeclaration of variable 'apic', originally seen at DIE 7a9c.
+warning: .debug_info: DIE 0x7f16: Redeclaration of variable 'per_cpu__x86_bios_cpu_apicid', originally seen at DIE 7ab0.
+warning: .debug_info: DIE 0x7f23: Redeclaration of variable 'per_cpu__cpu_sibling_map', originally seen at DIE 7abe.
+warning: .debug_info: DIE 0x7f30: Redeclaration of variable 'per_cpu__cpu_core_map', originally seen at DIE 7acb.
+warning: .debug_info: DIE 0x7f3d: Redeclaration of variable 'per_cpu__cpu_number', originally seen at DIE 7ad8.
+warning: .debug_info: DIE 0x7f4a: Redeclaration of variable 'smp_ops', originally seen at DIE 7ae5.
+warning: .debug_info: DIE 0x7f57: Redeclaration of variable 'memnode', originally seen at DIE 7af2.
+warning: .debug_info: DIE 0x7f64: Redeclaration of variable 'mem_section', originally seen at DIE 7b16.
+warning: .debug_info: DIE 0x7f72: Redeclaration of variable 'per_cpu__x86_cpu_to_node_map', originally seen at DIE 7b24.
+warning: .debug_info: DIE 0x7f7f: Redeclaration of variable 'x86_cpu_to_node_map_early_ptr', originally seen at DIE 7b31.
+warning: .debug_info: DIE 0x7f8c: Redeclaration of variable 'per_cpu__node_number', originally seen at DIE 7b3e.
+warning: .debug_info: DIE 0x7f99: Redeclaration of variable 'node_to_cpumask_map', originally seen at DIE 7b62.
+warning: .debug_info: DIE 0x7fa6: Redeclaration of variable 'gfp_allowed_mask', originally seen at DIE 7b6f.
+warning: .debug_info: DIE 0x7fb4: Redeclaration of variable '__tracepoint_kmalloc', originally seen at DIE 7b7d.
+warning: .debug_info: DIE 0x7fc1: Redeclaration of variable '__tracepoint_kmem_cache_alloc', originally seen at DIE 7b8a.
+warning: .debug_info: DIE 0x7fce: Redeclaration of variable '__tracepoint_kmalloc_node', originally seen at DIE 7b97.
+warning: .debug_info: DIE 0x7fdb: Redeclaration of variable '__tracepoint_kmem_cache_alloc_node', originally seen at DIE 7ba4.
+warning: .debug_info: DIE 0x7fe8: Redeclaration of variable '__tracepoint_kfree', originally seen at DIE 7bb1.
+warning: .debug_info: DIE 0x7ff5: Redeclaration of variable '__tracepoint_kmem_cache_free', originally seen at DIE 7bbe.
+warning: .debug_info: DIE 0x8002: Redeclaration of variable '__tracepoint_mm_page_free_direct', originally seen at DIE 7bcb.
+warning: .debug_info: DIE 0x800f: Redeclaration of variable '__tracepoint_mm_pagevec_free', originally seen at DIE 7bd8.
+warning: .debug_info: DIE 0x801c: Redeclaration of variable '__tracepoint_mm_page_alloc', originally seen at DIE 7be5.
+warning: .debug_info: DIE 0x802a: Redeclaration of variable '__tracepoint_mm_page_alloc_zone_locked', originally seen at DIE 7bf3.
+warning: .debug_info: DIE 0x8038: Redeclaration of variable '__tracepoint_mm_page_pcpu_drain', originally seen at DIE 7c01.
+warning: .debug_info: DIE 0x8046: Redeclaration of variable '__tracepoint_mm_page_alloc_extfrag', originally seen at DIE 7c0f.
+warning: .debug_info: DIE 0x8054: Redeclaration of variable 'kmalloc_caches', originally seen at DIE 7c2d.
+warning: .debug_info: DIE 0x8061: Redeclaration of variable '__tracepoint_module_load', originally seen at DIE 7c3a.
+warning: .debug_info: DIE 0x806e: Redeclaration of variable '__tracepoint_module_free', originally seen at DIE 7c47.
+warning: .debug_info: DIE 0x807b: Redeclaration of variable '__tracepoint_module_get', originally seen at DIE 7c54.
+warning: .debug_info: DIE 0x8088: Redeclaration of variable '__tracepoint_module_put', originally seen at DIE 7c61.
+warning: .debug_info: DIE 0x8095: Redeclaration of variable '__tracepoint_module_request', originally seen at DIE 7c6e.
+EOF
diff --git a/dwarflint/tests/run-check_range_out_of_scope.sh b/dwarflint/tests/run-check_range_out_of_scope.sh
new file mode 100755 (executable)
index 0000000..fa3b2de
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/sh
+# Copyright (C) 2010 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles check_range_out_of_scope-1
+
+testrun_compare ./dwarflint --check=check_range_out_of_scope check_range_out_of_scope-1 <<EOF
+error: .debug_info: DIE 0x8b: PC range [0x4004d0, 0x4004d1) is not a sub-range of containing scope.
+error: .debug_info: DIE 0x7a: in this context: [0x4004d4, 0x4004db)
+EOF
diff --git a/dwarflint/tests/run-check_self_referential_die.sh b/dwarflint/tests/run-check_self_referential_die.sh
new file mode 100755 (executable)
index 0000000..7fe83c6
--- /dev/null
@@ -0,0 +1,26 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles check_self_referential_die
+
+testrun_compare ./dwarflint --check=check_self_referential_die --ignore-bloat check_self_referential_die <<EOF
+warning: .debug_info: DIE 0x19dc2: structure_type attribute containing_type references DIE itself.
+EOF
diff --git a/dwarflint/tests/run-debug_abbrev-duplicate-attribute.sh b/dwarflint/tests/run-debug_abbrev-duplicate-attribute.sh
new file mode 100755 (executable)
index 0000000..b189bee
--- /dev/null
@@ -0,0 +1,29 @@
+#! /bin/sh
+# Copyright (C) 2010 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles debug_abbrev-duplicate-attribute
+
+testrun_compare ./dwarflint debug_abbrev-duplicate-attribute <<EOF
+error: .debug_abbrev: abbr. attribute 0x19: duplicate attribute byte_size (first was at 0x13).
+error: .debug_abbrev: abbr. attribute 0x1b: duplicate attribute decl_file (first was at 0x15).
+error: .debug_abbrev: abbr. attribute 0x1d: duplicate attribute decl_line (first was at 0x17).
+warning: .debug_info: DIE 0xb: DW_AT_low_pc value not below DW_AT_high_pc.
+EOF
diff --git a/dwarflint/tests/run-libdl-2.12.so.debug.sh b/dwarflint/tests/run-libdl-2.12.so.debug.sh
new file mode 100755 (executable)
index 0000000..f9f3363
--- /dev/null
@@ -0,0 +1,43 @@
+#! /bin/sh
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles libdl-2.12.so.debug
+
+# Here we test that dwarflint can tolerate invalid attribute name.
+testrun_compare ./dwarflint --check=@low --nognu --ignore-bloat libdl-2.12.so.debug <<EOF
+warning: .debug_abbrev: abbr. attribute 0xbe: invalid or unknown name 0x2107.
+warning: .debug_abbrev: abbr. attribute 0x330: invalid or unknown name 0x2107.
+warning: .debug_abbrev: abbr. attribute 0xa28: invalid or unknown name 0x2107.
+warning: .debug_abbrev: abbr. attribute 0x108e: invalid or unknown name 0x2107.
+warning: .debug_abbrev: abbr. attribute 0x1300: invalid or unknown name 0x2107.
+warning: .debug_info: DIE 0xd9a8: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: DIE 0xdcd7: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: CU 55709: no aranges table is associated with this CU.
+warning: .debug_info: CU 56524: no aranges table is associated with this CU.
+EOF
+
+# Here we test proper support for DW_AT_GNU_vector
+testrun_compare ./dwarflint --check=@low --ignore-bloat libdl-2.12.so.debug <<EOF
+warning: .debug_info: DIE 0xd9a8: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: DIE 0xdcd7: DW_AT_low_pc value not below DW_AT_high_pc.
+warning: .debug_info: CU 55709: no aranges table is associated with this CU.
+warning: .debug_info: CU 56524: no aranges table is associated with this CU.
+EOF
diff --git a/dwarflint/tests/run-location-leaks.sh b/dwarflint/tests/run-location-leaks.sh
new file mode 100755 (executable)
index 0000000..a8eb63f
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/sh
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles location-leaks
+
+testrun_compare ./dwarflint location-leaks <<EOF
+warning: .debug_loc: loclist 0x38: entry covers no range.
+error: .debug_info: DIE 0x62: attribute \`location': PC range [0x400495, 0x40049a) outside containing scope.
+error: .debug_info: DIE 0x51: in this context: [0x400498, 0x4004b2).
+EOF
diff --git a/dwarflint/tests/run-nodebug.sh b/dwarflint/tests/run-nodebug.sh
new file mode 100755 (executable)
index 0000000..1b67a70
--- /dev/null
@@ -0,0 +1,73 @@
+#! /bin/sh
+# Copyright (C) 2010, 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles nodebug null.o
+
+testrun_compare ./dwarflint nodebug <<EOF
+error: .debug_abbrev: data not found.
+error: .debug_info: data not found.
+EOF
+
+testrun_compare ./dwarflint -i nodebug <<EOF
+No errors
+EOF
+
+testrun_compare ./dwarflint -q -i nodebug <<EOF
+EOF
+
+# This has nothing to do with the nodebug test, but we can just as
+# well stick it in there.
+testrun_compare ./dwarflint --check=oentuh -q nodebug <<EOF
+warning: the rule \`oentuh' never matched.
+EOF
+
+# ... and since we are testing this here, also check that we don't get
+# this message in situations where it makes no sense.
+LANG=C testrun_compare ./dwarflint --check=oentuh -q noeuht <<EOF
+error: Cannot open input file: No such file or directory.
+EOF
+
+LANG=C testrun_compare ./dwarflint --check=oentuh -q noeuht nodebug <<EOF
+
+noeuht:
+error: Cannot open input file: No such file or directory.
+
+nodebug:
+warning: the rule \`oentuh' never matched.
+EOF
+
+LANG=C testrun_compare ./dwarflint --check=oentuh -q nodebug nodebug <<EOF
+
+nodebug:
+
+nodebug:
+warning: the rule \`oentuh' never matched.
+EOF
+
+testrun_compare ./dwarflint null.o <<EOF
+warning: .debug_abbrev: [0x0, 0x1): unnecessary padding with zero bytes.
+warning: .debug_abbrev: no abbreviations.
+error: .debug_info: data not found.
+EOF
+
+testrun_compare ./dwarflint --ignore-bloat --nodebug:ignore null.o <<EOF
+No errors
+EOF
diff --git a/dwarflint/tests/run-test-all-dies-it.sh b/dwarflint/tests/run-test-all-dies-it.sh
new file mode 100755 (executable)
index 0000000..fdcf63f
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+testfiles hello.bad-2
+
+../src/readelf -winfo ./tests/test-all-dies-it | grep '^ \[ *[0-9a-f]*\]' |
+  sed 's/ \[ *\([0-9a-f]\+\).*/0x\1/' |
+  testrun_compare ./tests/test-all-dies-it ./tests/test-all-dies-it
+
+testrun_compare ./tests/test-all-dies-it hello.bad-2 <<EOF
+0xb
+EOF
diff --git a/dwarflint/tests/run-upper.sh b/dwarflint/tests/run-upper.sh
new file mode 100755 (executable)
index 0000000..d1ed96a
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/sh
+# Copyright (C) 2011 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/../tests/test-subr.sh
+
+srcdir=$srcdir/tests
+
+# Following program compiled with "default" gcc settings,
+# which is dwarf-2 + gnu extensions. Which will result in:
+#
+#  [    a2]      subrange_type
+#                type                 (ref4) [    ac]
+#                upper_bound          (block1)
+#                [   0] fbreg -24
+#                [   2] deref
+#
+# According to dwarf-2 DW_AT_upperbound cannot be encoded with block form.
+# It can however with later versions of dwarf, which gcc will output as
+# gnu extension (unless -gstrict-dwarf is given).
+#
+# int max_range = 42;
+#
+# int main (int argc, char **argv)
+# {
+#   char chars[max_range];
+#   chars[max_range -1] = 7;
+#   return 0;
+# }
+#
+# This would crash the low-level check_debug_info in the past.
+testfiles upper
+
+testrun_compare ./dwarflint --quiet --check=@low upper <<EOF
+EOF
diff --git a/dwarflint/tests/test-all-dies-it.cc b/dwarflint/tests/test-all-dies-it.cc
new file mode 100644 (file)
index 0000000..ec24297
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <cassert>
+
+#include "config.h"
+#include "../all-dies-it.hh"
+#include "../../libdw/c++/dwarf"
+
+using namespace elfutils;
+
+int
+main (int argc, char ** argv)
+{
+  assert (argc == 2);
+  char const *fn = argv[1];
+  assert (fn != NULL);
+
+  int fd = open (fn, O_RDONLY);
+  assert (fd >= 0);
+
+  Dwarf *cdw = dwarf_begin (fd, DWARF_C_READ);
+  assert (cdw != NULL);
+
+  dwarf dw = cdw;
+  for (all_dies_iterator<dwarf> it = all_dies_iterator<dwarf> (dw);
+       it != all_dies_iterator<dwarf> (); ++it)
+    std::cout << std::hex << "0x" << (*it).offset () << std::endl;
+}
diff --git a/dwarflint/tests/test-coverage.cc b/dwarflint/tests/test-coverage.cc
new file mode 100644 (file)
index 0000000..3d3add0
--- /dev/null
@@ -0,0 +1,166 @@
+#include <iostream>
+#include <cstdlib>
+#include <cassert>
+#include "../coverage.hh"
+#include "../pri.hh"
+
+bool fail = false;
+
+void
+cmpfmt (coverage const &cov,
+       std::string const &exp)
+{
+  std::string act = cov::format_ranges (cov);
+  if (act != exp)
+    std::cerr << "FAIL: expected: " << exp << std::endl
+             << "           got: " << act << std::endl;
+}
+
+void
+cmpholes (coverage const &cov,
+         std::string const &exp)
+{
+  uint64_t start = cov.front ().start;
+  uint64_t len = cov.back ().end () - start;
+  std::string act = cov::format_holes (cov, start, len);
+  if (act != exp)
+    std::cerr << "FAIL: expected: " << exp << std::endl
+             << "           got: " << act << std::endl;
+}
+
+void
+chkcov (coverage const &cov, uint64_t start, uint64_t length)
+{
+  assert (cov.is_covered (start, length));
+  assert (cov.is_overlap (start, length));
+  for (uint64_t i = start; i < start + length; ++i)
+    {
+      assert (cov.is_covered (i, 1));
+      for (unsigned k = 0; k < 100; ++k)
+       {
+         assert (cov.is_overlap (i, k + 1));
+         if (i >= k)
+           {
+             assert (cov.is_overlap (i - k, k + 1));
+             assert (cov.is_overlap (i - k, 2*k + 1));
+           }
+       }
+    }
+}
+
+void
+chkncov (coverage const &cov, uint64_t start, uint64_t length)
+{
+  assert (!cov.is_overlap (start, length));
+  for (uint64_t i = start; i < start + length; ++i)
+    {
+      assert (!cov.is_covered (i, 1));
+      assert (!cov.is_overlap (i, 1));
+    }
+}
+
+class check_assert_used
+{
+  bool _m_used;
+
+public:
+  check_assert_used ()
+    : _m_used (false)
+  {
+    assert (_m_used = true);
+    if (!_m_used)
+      abort ();
+  }
+};
+
+int
+main ()
+{
+  check_assert_used ();
+
+  coverage cov;
+  assert (cov.empty ());
+  cmpfmt(cov, "");
+  chkncov (cov, 0x0, 0x100);
+
+  cov.add (0x10, 0x20);
+  chkncov (cov, 0x0, 0x10);
+  chkcov (cov, 0x10, 0x20);
+  chkncov (cov, 0x30, 0x100);
+  cmpfmt(cov, "[0x10, 0x30)");
+  cmpholes(cov, "");
+
+  cov.add (0x40, 0x20);
+  chkncov (cov, 0x0, 0x10);
+  chkcov (cov, 0x10, 0x20);
+  chkncov (cov, 0x30, 0x10);
+  chkcov (cov, 0x40, 0x20);
+  chkncov (cov, 0x60, 0x100);
+  cmpfmt(cov, "[0x10, 0x30), [0x40, 0x60)");
+  cmpholes(cov, "[0x30, 0x40)");
+
+  cov.add (0x50, 0x20);
+  cmpfmt(cov, "[0x10, 0x30), [0x40, 0x70)");
+  cmpholes(cov, "[0x30, 0x40)");
+
+  cov.add (5, 1);
+  cmpfmt(cov, "[0x5, 0x6), [0x10, 0x30), [0x40, 0x70)");
+  cmpholes(cov, "[0x6, 0x10), [0x30, 0x40)");
+
+  cov.add (5, 1);
+  cmpfmt(cov, "[0x5, 0x6), [0x10, 0x30), [0x40, 0x70)");
+  cmpholes(cov, "[0x6, 0x10), [0x30, 0x40)");
+
+  cov.add (0, 5);
+  cmpfmt(cov, "[0x0, 0x6), [0x10, 0x30), [0x40, 0x70)");
+  cmpholes(cov, "[0x6, 0x10), [0x30, 0x40)");
+
+  {
+    coverage cov2 = cov;
+    cov2.add (0, 0x40);
+    cmpfmt(cov2, "[0x0, 0x70)");
+  }
+  cov.add (0, 0x30);
+  cmpfmt(cov, "[0x0, 0x30), [0x40, 0x70)");
+  cov.add (0x31, 5);
+  cmpfmt(cov, "[0x0, 0x30), [0x31, 0x36), [0x40, 0x70)");
+
+  assert (cov.remove (0x40, 0x30));
+  cmpfmt(cov, "[0x0, 0x30), [0x31, 0x36)");
+  assert (!cov.remove (0x30, 1));
+  cmpfmt(cov, "[0x0, 0x30), [0x31, 0x36)");
+  assert (cov.remove (0x2f, 3));
+  cmpfmt(cov, "[0x0, 0x2f), [0x32, 0x36)");
+  assert (cov.remove (0x10, 0x10));
+  cmpfmt(cov, "[0x0, 0x10), [0x20, 0x2f), [0x32, 0x36)");
+  assert (cov.remove (0x2, 3));
+  cmpfmt(cov, "[0x0, 0x2), [0x5, 0x10), [0x20, 0x2f), [0x32, 0x36)");
+  cmpholes(cov, "[0x2, 0x5), [0x10, 0x20), [0x2f, 0x32)");
+  assert (cov.remove (0x1, 0x40));
+  cmpfmt(cov, "[0x0, 0x1)");
+  assert (cov.remove (0x0, 0x40));
+  assert (cov.empty ());
+  cmpfmt(cov, "");
+
+  cov.add (0, 10);
+  assert (cov == cov);
+  assert (cov == coverage (cov));
+  assert (cov == coverage (cov) + coverage (cov));
+  assert ((coverage (cov) - coverage (cov)).empty ());
+
+  cov.add (20, 0);
+  cmpfmt (cov, "[0x0, 0xa), [0x14, 0x14)");
+  chkcov (cov, 20, 0);
+  chkcov (cov, 0, 0);
+  chkcov (cov, 9, 0);
+  chkcov (cov, 10, 0);
+  chkncov (cov, 11, 0);
+  chkncov (cov, 19, 1);
+  chkncov (cov, 20, 1);
+  chkncov (cov, 30, 0);
+  cov.add (30, 10);
+  cmpholes(cov, "[0xa, 0x14), [0x14, 0x1e)");
+
+  if (fail)
+    std::exit (1);
+}
diff --git a/dwarflint/tests/test-wrap.cc b/dwarflint/tests/test-wrap.cc
new file mode 100644 (file)
index 0000000..57bfda2
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "../wrap.hh"
+#include <cassert>
+
+std::string
+wrap (char const *str, size_t width)
+{
+  return wrap_str (str, width).join ();
+}
+
+std::string
+sspaces (size_t i)
+{
+  return spaces (i);
+}
+
+int main (void)
+{
+  assert (sspaces (0) == "");
+  assert (sspaces (1) == " ");
+  assert (sspaces (2) == "  ");
+  assert (sspaces (10) == "          ");
+  assert (wrap ("a b c d", 1) == "a\nb\nc\nd\n");
+  assert (wrap ("a bbbbb c d", 1) == "a\nbbbbb\nc\nd\n");
+  assert (wrap ("a b", 3) == "a b\n");
+  assert (wrap (" a b", 3) == " a\n b\n");
+  assert (wrap (" a b", 4) == " a b\n");
+  assert (wrap (" a b c d", 4) == " a b\n c d\n");
+  assert (wrap ("ab cd ef gh ij", 2) == "ab\ncd\nef\ngh\nij\n");
+  assert (wrap ("ab cd ef gh ij", 3) == "ab\ncd\nef\ngh\nij\n");
+  assert (wrap ("ab cd ef gh ij", 4) == "ab\ncd\nef\ngh\nij\n");
+  assert (wrap ("ab cd ef gh ij", 5) == "ab cd\nef gh\nij\n");
+  assert (wrap ("", 5) == "");
+  assert (wrap ("", 0) == "");
+  assert (wrap ("\n", 5) == "\n");
+  assert (wrap ("\n\n", 5) == "\n\n");
+  assert (wrap ("\n\n", 0) == "\n\n");
+  assert (wrap ("ab\ncd ef gh ij", 5) == "ab\ncd ef\ngh ij\n");
+  assert (wrap (" - abcd abbb accc", 3) == " - abcd\n   abbb\n   accc\n");
+  assert (wrap (" -abcd abbb accc", 3) == " -abcd\n  abbb\n  accc\n");
+  assert (wrap ("  abcd abbb accc", 3) == "  abcd\n  abbb\n  accc\n");
+}
diff --git a/dwarflint/tests/upper.bz2 b/dwarflint/tests/upper.bz2
new file mode 100755 (executable)
index 0000000..8844886
Binary files /dev/null and b/dwarflint/tests/upper.bz2 differ
diff --git a/dwarflint/wrap.cc b/dwarflint/wrap.cc
new file mode 100644 (file)
index 0000000..6d40ee0
--- /dev/null
@@ -0,0 +1,168 @@
+/* Pedantic checking of DWARF files
+
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "wrap.hh"
+#include <cassert>
+#include <cstring>
+
+namespace
+{
+  size_t
+  skip_blank (char const *str, size_t pos)
+  {
+    while (isblank (str[pos]))
+      pos++;
+    return pos;
+  }
+}
+
+wrapline_t::wrapline_t (size_t start, size_t end, size_t indent)
+  : _m_start (start)
+  , _m_end (end)
+  , _m_indent (indent)
+{
+  assert (end >= start);
+}
+
+std::string
+wrapline_t::build (char const *image) const
+{
+  assert (_m_end <= std::strlen (image));
+  std::string main (image, _m_start, _m_end - _m_start);
+  char const *padding = spaces (_m_indent);
+  return std::string (padding) + main;
+}
+
+wrap_str::wrap_str (char const *str, unsigned width)
+  : _m_image (str)
+{
+  size_t pos = 0;
+  bool newline = true;
+  size_t indent = 0;
+  size_t str_size = std::strlen (str);
+  while (pos < str_size)
+    {
+      size_t last = pos;
+
+      // This is how long a line we can allocate.
+      size_t length = width;
+
+      // For newlines, i.e. right after hard EOL (\n) in input string,
+      // we look for indentation and bullets.
+      if (newline)
+       {
+         pos = skip_blank (str, pos);
+         if (pos < str_size && str[pos] == '-')
+           pos = skip_blank (str, pos + 1);
+         indent = pos - last;
+       }
+      length -= indent;
+
+      // Take the remainder of the line, but don't cross hard EOLs.
+      for (; length > 0 && pos < str_size; --length)
+       if (str[pos] == '\n')
+         break;
+       else
+         pos++;
+
+      // We may have ended mid-word.  Look back to first white space.
+      // Look as far back as the end of previous line.
+      size_t space = pos;
+      for (; space > last; --space)
+       if (space == str_size || isspace (str[space]))
+         break;
+
+      // While skipping back, we might end at the very beginning.  If
+      // that's the case, we have a word that doesn't fit user limit.
+      // Include it whole anyway.  For newline, account for
+      // freshly-introduced indent.
+      if (space <= last + (newline ? indent : 0))
+       {
+         space = pos;
+         while (space < str_size && !isspace (str[space]))
+           space++;
+       }
+
+      // We have a line!
+      push_back (wrapline_t (last, space, newline ? 0 : indent));
+
+      // Skip useless white space at the end of the line, up to EOL.
+      while (space < str_size && isspace (str[space]) && str[space] != '\n')
+       space++;
+
+      if (str[space] == '\n')
+       {
+         space++;
+         indent = 0;
+         newline = true;
+       }
+      else
+       newline = false;
+
+      pos = space;
+    }
+}
+
+std::string
+wrap_str::join () const
+{
+  std::string ret;
+  for (const_iterator it = begin (); it != end (); ++it)
+    ret += build (it) + "\n";
+  return ret;
+}
+
+std::string
+wrap_str::build (wrap_str::const_iterator it) const
+{
+  return it->build (_m_image);
+}
+
+namespace
+{
+  template <unsigned Max>
+  class spc
+  {
+    char *_m_buf;
+    char *_m_endp;
+  public:
+    spc ()
+      : _m_buf (new char[Max])
+      , _m_endp (_m_buf + Max - 1)
+    {
+      std::memset (_m_buf, ' ', Max - 1);
+      _m_buf[Max - 1] = 0;
+    }
+    ~spc ()
+    {
+      delete [] _m_buf;
+    }
+    char const *get (size_t n)
+    {
+      assert (n < Max);
+      return _m_endp - n;
+    }
+  };
+}
+
+char const *
+spaces (size_t n)
+{
+  static spc<128> spaces;
+  return spaces.get (n);
+}
diff --git a/dwarflint/wrap.hh b/dwarflint/wrap.hh
new file mode 100644 (file)
index 0000000..d9e052b
--- /dev/null
@@ -0,0 +1,58 @@
+/* Pedantic checking of DWARF files
+
+   Copyright (C) 2010 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DWARFLINT_WRAP_HH
+#define DWARFLINT_WRAP_HH
+
+#include <string>
+#include <vector>
+
+class wrapline_t
+{
+  size_t _m_start;
+  size_t _m_end;
+  size_t _m_indent;
+
+public:
+  wrapline_t (size_t start, size_t end, size_t indent);
+  std::string build (char const *image) const;
+};
+
+class wrap_str
+  : private std::vector<wrapline_t>
+{
+  char const *_m_image;
+
+public:
+  typedef std::vector<wrapline_t> super_t;
+  using super_t::operator [];
+  using super_t::size;
+  using super_t::const_iterator;
+  using super_t::begin;
+  using super_t::end;
+  using super_t::empty;
+
+  wrap_str (char const *str, unsigned width);
+
+  std::string join () const;
+  std::string build (const_iterator it) const;
+};
+
+char const *spaces (size_t n);
+
+#endif//DWARFLINT_WRAP_HH