summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDjordje Todorovic <djordje.todorovic@rt-rk.com>2017-09-26 14:21:47 +0200
committerMark Wielaard <mark@klomp.org>2017-10-06 14:41:37 +0200
commit20aa8539966069195ace5f909e6f5cf8b4e56e2c (patch)
tree11b61cc49b4d3b596b26eacc7f1f73f6edfc7296
parent129765d3887ab0c7ce9ecb58eb836e8bc739239c (diff)
downloadelfutils-20aa8539966069195ace5f909e6f5cf8b4e56e2c.tar.gz
Add dwarflint/ from origin/dwarf branch
Signed-off-by: Djordje Todorovic <djordje.todorovic@rt-rk.com>
-rw-r--r--dwarflint/Makefile.am209
-rw-r--r--dwarflint/TODO161
-rw-r--r--dwarflint/addr-record.cc60
-rw-r--r--dwarflint/addr-record.hh74
-rw-r--r--dwarflint/all-dies-it.hh142
-rw-r--r--dwarflint/check_debug_abbrev.cc574
-rw-r--r--dwarflint/check_debug_abbrev.hh144
-rw-r--r--dwarflint/check_debug_abbrev_i.hh1
-rw-r--r--dwarflint/check_debug_aranges.cc448
-rw-r--r--dwarflint/check_debug_aranges.hh98
-rw-r--r--dwarflint/check_debug_aranges_i.hh1
-rw-r--r--dwarflint/check_debug_info.cc1261
-rw-r--r--dwarflint/check_debug_info.hh161
-rw-r--r--dwarflint/check_debug_info_i.hh5
-rw-r--r--dwarflint/check_debug_line.cc703
-rw-r--r--dwarflint/check_debug_line.hh44
-rw-r--r--dwarflint/check_debug_line_i.hh1
-rw-r--r--dwarflint/check_debug_loc_range.cc1041
-rw-r--r--dwarflint/check_debug_loc_range.hh112
-rw-r--r--dwarflint/check_debug_loc_range_i.hh3
-rw-r--r--dwarflint/check_debug_pub.cc260
-rw-r--r--dwarflint/check_debug_pub.hh61
-rw-r--r--dwarflint/check_die_decl_call.cc105
-rw-r--r--dwarflint/check_die_line_info.cc136
-rw-r--r--dwarflint/check_die_tree.cc147
-rw-r--r--dwarflint/check_die_tree.hh109
-rw-r--r--dwarflint/check_die_tree_i.hh1
-rw-r--r--dwarflint/check_duplicate_DW_tag_variable.cc126
-rw-r--r--dwarflint/check_dups_abstract_origin.cc152
-rw-r--r--dwarflint/check_expected_trees.cc202
-rw-r--r--dwarflint/check_linkage_external_die.cc171
-rw-r--r--dwarflint/check_matching_ranges.cc108
-rw-r--r--dwarflint/check_nodebug.cc73
-rw-r--r--dwarflint/check_range_out_of_scope.cc233
-rw-r--r--dwarflint/check_registrar.hh57
-rw-r--r--dwarflint/check_registrar_i.hh2
-rw-r--r--dwarflint/check_self_referential_die.cc72
-rw-r--r--dwarflint/checkdescriptor.cc125
-rw-r--r--dwarflint/checkdescriptor.hh100
-rw-r--r--dwarflint/checkdescriptor_i.hh1
-rw-r--r--dwarflint/checked_read.cc209
-rw-r--r--dwarflint/checked_read.hh64
-rw-r--r--dwarflint/checkrule.cc82
-rw-r--r--dwarflint/checkrule.hh62
-rw-r--r--dwarflint/checks.hh99
-rw-r--r--dwarflint/checks_i.hh2
-rw-r--r--dwarflint/coverage.cc367
-rw-r--r--dwarflint/coverage.hh142
-rw-r--r--dwarflint/cu_coverage.cc42
-rw-r--r--dwarflint/cu_coverage.hh41
-rw-r--r--dwarflint/cu_coverage_i.hh1
-rw-r--r--dwarflint/die_locus.cc45
-rw-r--r--dwarflint/die_locus.hh60
-rw-r--r--dwarflint/dwarf_2.cc169
-rw-r--r--dwarflint/dwarf_2.hh26
-rw-r--r--dwarflint/dwarf_3.cc141
-rw-r--r--dwarflint/dwarf_3.hh30
-rw-r--r--dwarflint/dwarf_4.cc97
-rw-r--r--dwarflint/dwarf_4.hh30
-rw-r--r--dwarflint/dwarf_gnu.cc117
-rw-r--r--dwarflint/dwarf_gnu.hh26
-rw-r--r--dwarflint/dwarf_mips.cc88
-rw-r--r--dwarflint/dwarf_mips.hh26
-rw-r--r--dwarflint/dwarf_version-imp.cc69
-rw-r--r--dwarflint/dwarf_version-imp.hh114
-rw-r--r--dwarflint/dwarf_version.cc293
-rw-r--r--dwarflint/dwarf_version.hh240
-rw-r--r--dwarflint/dwarf_version_i.hh3
-rw-r--r--dwarflint/dwarflint.cc245
-rw-r--r--dwarflint/dwarflint.hh136
-rw-r--r--dwarflint/dwarflint_i.hh3
-rw-r--r--dwarflint/elf_file.hh62
-rw-r--r--dwarflint/elf_file_i.hh4
-rw-r--r--dwarflint/expected-at.cc868
-rw-r--r--dwarflint/expected.hh102
-rw-r--r--dwarflint/files.cc138
-rw-r--r--dwarflint/files.hh39
-rw-r--r--dwarflint/highlevel_check.cc33
-rw-r--r--dwarflint/highlevel_check.hh79
-rw-r--r--dwarflint/highlevel_check_i.hh1
-rw-r--r--dwarflint/locstats.cc657
-rw-r--r--dwarflint/locus.cc64
-rw-r--r--dwarflint/locus.hh104
-rw-r--r--dwarflint/lowlevel_checks.cc87
-rw-r--r--dwarflint/lowlevel_checks.hh32
-rw-r--r--dwarflint/main.cc224
-rw-r--r--dwarflint/main.hh25
-rw-r--r--dwarflint/messages.cc491
-rw-r--r--dwarflint/messages.hh233
-rw-r--r--dwarflint/misc.cc60
-rw-r--r--dwarflint/misc.hh54
-rw-r--r--dwarflint/option.cc223
-rw-r--r--dwarflint/option.hh234
-rw-r--r--dwarflint/option_i.hh1
-rw-r--r--dwarflint/pri.cc65
-rw-r--r--dwarflint/pri.hh113
-rw-r--r--dwarflint/readctx.cc342
-rw-r--r--dwarflint/readctx.hh73
-rw-r--r--dwarflint/reloc.cc510
-rw-r--r--dwarflint/reloc.hh151
-rw-r--r--dwarflint/section_id.cc29
-rw-r--r--dwarflint/section_id.hh49
-rw-r--r--dwarflint/sections.cc444
-rw-r--r--dwarflint/sections.hh72
-rw-r--r--dwarflint/sections_i.hh2
-rw-r--r--dwarflint/tests/DW_AT-later-version.bz2bin0 -> 591 bytes
-rw-r--r--dwarflint/tests/DW_AT_high_pc-below.bz2bin0 -> 614 bytes
-rw-r--r--dwarflint/tests/DW_AT_high_pc-relative.bz2bin0 -> 615 bytes
-rwxr-xr-xdwarflint/tests/aranges_terminate_early.bz2bin0 -> 2661 bytes
-rwxr-xr-xdwarflint/tests/check_debug_info_refs-1.bz2bin0 -> 2704 bytes
-rw-r--r--dwarflint/tests/check_debug_info_refs-2.bz2bin0 -> 2686 bytes
-rwxr-xr-xdwarflint/tests/check_range_out_of_scope-1.bz2bin0 -> 2717 bytes
-rw-r--r--dwarflint/tests/check_self_referential_die.bz2bin0 -> 221376 bytes
-rw-r--r--dwarflint/tests/crc7.ko.debug.bz2bin0 -> 30999 bytes
-rw-r--r--dwarflint/tests/debug_abbrev-duplicate-attribute.bz2bin0 -> 616 bytes
-rw-r--r--dwarflint/tests/empty-1.bz2bin0 -> 587 bytes
-rw-r--r--dwarflint/tests/garbage-1.bz2bin0 -> 2768 bytes
-rw-r--r--dwarflint/tests/garbage-10.bz2bin0 -> 596 bytes
-rw-r--r--dwarflint/tests/garbage-11.bz2bin0 -> 592 bytes
-rw-r--r--dwarflint/tests/garbage-12.bz2bin0 -> 598 bytes
-rw-r--r--dwarflint/tests/garbage-2.bz2bin0 -> 2764 bytes
-rw-r--r--dwarflint/tests/garbage-3.bz2bin0 -> 2765 bytes
-rw-r--r--dwarflint/tests/garbage-4.bz2bin0 -> 2765 bytes
-rw-r--r--dwarflint/tests/garbage-5.bz2bin0 -> 2780 bytes
-rw-r--r--dwarflint/tests/garbage-6.bz2bin0 -> 2787 bytes
-rw-r--r--dwarflint/tests/garbage-7.bz2bin0 -> 2762 bytes
-rw-r--r--dwarflint/tests/garbage-8.bz2bin0 -> 2753 bytes
-rw-r--r--dwarflint/tests/garbage-9.bz2bin0 -> 2763 bytes
-rw-r--r--dwarflint/tests/hello.bad-1.bz2bin0 -> 2691 bytes
-rw-r--r--dwarflint/tests/hello.bad-1.s246
-rw-r--r--dwarflint/tests/hello.bad-2.bz2bin0 -> 2703 bytes
-rw-r--r--dwarflint/tests/hello.bad-2.s246
-rw-r--r--dwarflint/tests/hello.bad-3.bz2bin0 -> 2737 bytes
-rw-r--r--dwarflint/tests/hello.bad-3.s313
-rw-r--r--dwarflint/tests/libdl-2.12.so.debug.bz2bin0 -> 28362 bytes
-rw-r--r--dwarflint/tests/location-leaks.bz2bin0 -> 2781 bytes
-rwxr-xr-xdwarflint/tests/nodebug.bz2bin0 -> 1647 bytes
-rw-r--r--dwarflint/tests/null.o.bz2bin0 -> 343 bytes
-rwxr-xr-xdwarflint/tests/run-DW_AT-later-version.sh28
-rwxr-xr-xdwarflint/tests/run-DW_AT_high_pc-below.sh28
-rwxr-xr-xdwarflint/tests/run-DW_AT_high_pc-relative.sh28
-rwxr-xr-xdwarflint/tests/run-aranges_terminate_early.sh34
-rwxr-xr-xdwarflint/tests/run-bad.sh126
-rwxr-xr-xdwarflint/tests/run-check_debug_info_refs.sh37
-rwxr-xr-xdwarflint/tests/run-check_duplicate_DW_tag_variable.sh166
-rwxr-xr-xdwarflint/tests/run-check_range_out_of_scope.sh27
-rwxr-xr-xdwarflint/tests/run-check_self_referential_die.sh26
-rwxr-xr-xdwarflint/tests/run-debug_abbrev-duplicate-attribute.sh29
-rwxr-xr-xdwarflint/tests/run-libdl-2.12.so.debug.sh43
-rwxr-xr-xdwarflint/tests/run-location-leaks.sh28
-rwxr-xr-xdwarflint/tests/run-nodebug.sh73
-rwxr-xr-xdwarflint/tests/run-test-all-dies-it.sh30
-rwxr-xr-xdwarflint/tests/run-upper.sh48
-rw-r--r--dwarflint/tests/test-all-dies-it.cc46
-rw-r--r--dwarflint/tests/test-coverage.cc166
-rw-r--r--dwarflint/tests/test-wrap.cc58
-rwxr-xr-xdwarflint/tests/upper.bz2bin0 -> 2905 bytes
-rw-r--r--dwarflint/wrap.cc168
-rw-r--r--dwarflint/wrap.hh58
159 files changed, 18270 insertions, 0 deletions
diff --git a/dwarflint/Makefile.am b/dwarflint/Makefile.am
new file mode 100644
index 00000000..e1a9c519
--- /dev/null
+++ b/dwarflint/Makefile.am
@@ -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
index 00000000..aa612026
--- /dev/null
+++ b/dwarflint/TODO
@@ -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
index 00000000..59dfa006
--- /dev/null
+++ b/dwarflint/addr-record.cc
@@ -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
index 00000000..a717d236
--- /dev/null
+++ b/dwarflint/addr-record.hh
@@ -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
index 00000000..fe30057d
--- /dev/null
+++ b/dwarflint/all-dies-it.hh
@@ -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
index 00000000..e7313230
--- /dev/null
+++ b/dwarflint/check_debug_abbrev.cc
@@ -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
index 00000000..ef5531ab
--- /dev/null
+++ b/dwarflint/check_debug_abbrev.hh
@@ -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
index 00000000..47c22088
--- /dev/null
+++ b/dwarflint/check_debug_abbrev_i.hh
@@ -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
index 00000000..8a503cef
--- /dev/null
+++ b/dwarflint/check_debug_aranges.cc
@@ -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
index 00000000..7393b8d2
--- /dev/null
+++ b/dwarflint/check_debug_aranges.hh
@@ -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
index 00000000..0a698133
--- /dev/null
+++ b/dwarflint/check_debug_aranges_i.hh
@@ -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
index 00000000..a7c51982
--- /dev/null
+++ b/dwarflint/check_debug_info.cc
@@ -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
index 00000000..1377e102
--- /dev/null
+++ b/dwarflint/check_debug_info.hh
@@ -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
index 00000000..ea388e39
--- /dev/null
+++ b/dwarflint/check_debug_info_i.hh
@@ -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
index 00000000..34047e27
--- /dev/null
+++ b/dwarflint/check_debug_line.cc
@@ -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
index 00000000..41fde291
--- /dev/null
+++ b/dwarflint/check_debug_line.hh
@@ -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
index 00000000..5d9a1ff7
--- /dev/null
+++ b/dwarflint/check_debug_line_i.hh
@@ -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
index 00000000..5f286226
--- /dev/null
+++ b/dwarflint/check_debug_loc_range.cc
@@ -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
index 00000000..4fbc6f70
--- /dev/null
+++ b/dwarflint/check_debug_loc_range.hh
@@ -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
index 00000000..a5926e12
--- /dev/null
+++ b/dwarflint/check_debug_loc_range_i.hh
@@ -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
index 00000000..8ac39283
--- /dev/null
+++ b/dwarflint/check_debug_pub.cc
@@ -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
index 00000000..5ee6bc6d
--- /dev/null
+++ b/dwarflint/check_debug_pub.hh
@@ -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
index 00000000..079d99e0
--- /dev/null
+++ b/dwarflint/check_die_decl_call.cc
@@ -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
index 00000000..359976f0
--- /dev/null
+++ b/dwarflint/check_die_line_info.cc
@@ -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
index 00000000..c9f0fb2a
--- /dev/null
+++ b/dwarflint/check_die_tree.cc
@@ -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
index 00000000..f8967809
--- /dev/null
+++ b/dwarflint/check_die_tree.hh
@@ -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
index 00000000..95b98e68
--- /dev/null
+++ b/dwarflint/check_die_tree_i.hh
@@ -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
index 00000000..609760b8
--- /dev/null
+++ b/dwarflint/check_duplicate_DW_tag_variable.cc
@@ -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
index 00000000..f81d957a
--- /dev/null
+++ b/dwarflint/check_dups_abstract_origin.cc
@@ -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
index 00000000..dd22ddfa
--- /dev/null
+++ b/dwarflint/check_expected_trees.cc
@@ -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
index 00000000..4170f9b2
--- /dev/null
+++ b/dwarflint/check_linkage_external_die.cc
@@ -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
index 00000000..972d0b67
--- /dev/null
+++ b/dwarflint/check_matching_ranges.cc
@@ -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
index 00000000..19455a59
--- /dev/null
+++ b/dwarflint/check_nodebug.cc
@@ -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
index 00000000..f60f18f3
--- /dev/null
+++ b/dwarflint/check_range_out_of_scope.cc
@@ -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
index 00000000..b254b018
--- /dev/null
+++ b/dwarflint/check_registrar.hh
@@ -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
index 00000000..6e84c911
--- /dev/null
+++ b/dwarflint/check_registrar_i.hh
@@ -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
index 00000000..48a6dca5
--- /dev/null
+++ b/dwarflint/check_self_referential_die.cc
@@ -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
index 00000000..99ce4db4
--- /dev/null
+++ b/dwarflint/checkdescriptor.cc
@@ -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
index 00000000..556eb972
--- /dev/null
+++ b/dwarflint/checkdescriptor.hh
@@ -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
index 00000000..052fe1bd
--- /dev/null
+++ b/dwarflint/checkdescriptor_i.hh
@@ -0,0 +1 @@
+struct checkdescriptor;
diff --git a/dwarflint/checked_read.cc b/dwarflint/checked_read.cc
new file mode 100644
index 00000000..48b15359
--- /dev/null
+++ b/dwarflint/checked_read.cc
@@ -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
index 00000000..c2b19e82
--- /dev/null
+++ b/dwarflint/checked_read.hh
@@ -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
index 00000000..7f14d726
--- /dev/null
+++ b/dwarflint/checkrule.cc
@@ -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
index 00000000..7c536d98
--- /dev/null
+++ b/dwarflint/checkrule.hh
@@ -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
index 00000000..7039b633
--- /dev/null
+++ b/dwarflint/checks.hh
@@ -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
index 00000000..2d405382
--- /dev/null
+++ b/dwarflint/checks_i.hh
@@ -0,0 +1,2 @@
+struct check_base;
+class die_check;
diff --git a/dwarflint/coverage.cc b/dwarflint/coverage.cc
new file mode 100644
index 00000000..9ce6954c
--- /dev/null
+++ b/dwarflint/coverage.cc
@@ -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
index 00000000..7d8fe285
--- /dev/null
+++ b/dwarflint/coverage.hh
@@ -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
index 00000000..fd941786
--- /dev/null
+++ b/dwarflint/cu_coverage.cc
@@ -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
index 00000000..c7a5b976
--- /dev/null
+++ b/dwarflint/cu_coverage.hh
@@ -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
index 00000000..01d567f3
--- /dev/null
+++ b/dwarflint/cu_coverage_i.hh
@@ -0,0 +1 @@
+class cu_coverage;
diff --git a/dwarflint/die_locus.cc b/dwarflint/die_locus.cc
new file mode 100644
index 00000000..a6a1667c
--- /dev/null
+++ b/dwarflint/die_locus.cc
@@ -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
index 00000000..bff85bea
--- /dev/null
+++ b/dwarflint/die_locus.hh
@@ -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
index 00000000..c73898e0
--- /dev/null
+++ b/dwarflint/dwarf_2.cc
@@ -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
index 00000000..84f11f26
--- /dev/null
+++ b/dwarflint/dwarf_2.hh
@@ -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
index 00000000..913029c3
--- /dev/null
+++ b/dwarflint/dwarf_3.cc
@@ -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
index 00000000..c3e017a4
--- /dev/null
+++ b/dwarflint/dwarf_3.hh
@@ -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
index 00000000..e398f769
--- /dev/null
+++ b/dwarflint/dwarf_4.cc
@@ -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
index 00000000..50d32c9a
--- /dev/null
+++ b/dwarflint/dwarf_4.hh
@@ -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
index 00000000..97f28d05
--- /dev/null
+++ b/dwarflint/dwarf_gnu.cc
@@ -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
index 00000000..1481c8cd
--- /dev/null
+++ b/dwarflint/dwarf_gnu.hh
@@ -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
index 00000000..43863335
--- /dev/null
+++ b/dwarflint/dwarf_mips.cc
@@ -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
index 00000000..0ec678fb
--- /dev/null
+++ b/dwarflint/dwarf_mips.hh
@@ -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
index 00000000..b4b20b56
--- /dev/null
+++ b/dwarflint/dwarf_version-imp.cc
@@ -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
index 00000000..8c170841
--- /dev/null
+++ b/dwarflint/dwarf_version-imp.hh
@@ -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...))
+ {}
+};
+
+
+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
index 00000000..57fd4b4b
--- /dev/null
+++ b/dwarflint/dwarf_version.cc
@@ -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
index 00000000..177743a1
--- /dev/null
+++ b/dwarflint/dwarf_version.hh
@@ -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
index 00000000..b1a54b5b
--- /dev/null
+++ b/dwarflint/dwarf_version_i.hh
@@ -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
index 00000000..8067bf41
--- /dev/null
+++ b/dwarflint/dwarflint.cc
@@ -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
index 00000000..6b828834
--- /dev/null
+++ b/dwarflint/dwarflint.hh
@@ -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
index 00000000..9769d6bd
--- /dev/null
+++ b/dwarflint/dwarflint_i.hh
@@ -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
index 00000000..34fb7c4a
--- /dev/null
+++ b/dwarflint/elf_file.hh
@@ -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
index 00000000..f1daaf0e
--- /dev/null
+++ b/dwarflint/elf_file_i.hh
@@ -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
index 00000000..2811130f
--- /dev/null
+++ b/dwarflint/expected-at.cc
@@ -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
index 00000000..cf1b3c5b
--- /dev/null
+++ b/dwarflint/expected.hh
@@ -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
index 00000000..fb74a0e4
--- /dev/null
+++ b/dwarflint/files.cc
@@ -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
index 00000000..a7baa3b4
--- /dev/null
+++ b/dwarflint/files.hh
@@ -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
index 00000000..071ad93f
--- /dev/null
+++ b/dwarflint/highlevel_check.cc
@@ -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
index 00000000..46b4d386
--- /dev/null
+++ b/dwarflint/highlevel_check.hh
@@ -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
index 00000000..ddf38e66
--- /dev/null
+++ b/dwarflint/highlevel_check_i.hh
@@ -0,0 +1 @@
+class highlevel_check_i;
diff --git a/dwarflint/locstats.cc b/dwarflint/locstats.cc
new file mode 100644
index 00000000..7602e7fd
--- /dev/null
+++ b/dwarflint/locstats.cc
@@ -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
index 00000000..105ef418
--- /dev/null
+++ b/dwarflint/locus.cc
@@ -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
index 00000000..f594f449
--- /dev/null
+++ b/dwarflint/locus.hh
@@ -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
index 00000000..13b92d93
--- /dev/null
+++ b/dwarflint/lowlevel_checks.cc
@@ -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
index 00000000..518bb850
--- /dev/null
+++ b/dwarflint/lowlevel_checks.hh
@@ -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
index 00000000..dc4abda8
--- /dev/null
+++ b/dwarflint/main.cc
@@ -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
index 00000000..96368f52
--- /dev/null
+++ b/dwarflint/main.hh
@@ -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
index 00000000..6b6cade8
--- /dev/null
+++ b/dwarflint/messages.cc
@@ -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
index 00000000..7a5828a1
--- /dev/null
+++ b/dwarflint/messages.hh
@@ -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
index 00000000..114d0d96
--- /dev/null
+++ b/dwarflint/misc.cc
@@ -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
index 00000000..f779ed61
--- /dev/null
+++ b/dwarflint/misc.hh
@@ -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
index 00000000..7248258c
--- /dev/null
+++ b/dwarflint/option.cc
@@ -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
index 00000000..2d3e427c
--- /dev/null
+++ b/dwarflint/option.hh
@@ -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
index 00000000..473d62a8
--- /dev/null
+++ b/dwarflint/option_i.hh
@@ -0,0 +1 @@
+class option_i;
diff --git a/dwarflint/pri.cc b/dwarflint/pri.cc
new file mode 100644
index 00000000..fc015428
--- /dev/null
+++ b/dwarflint/pri.cc
@@ -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
index 00000000..a1bb8ef6
--- /dev/null
+++ b/dwarflint/pri.hh
@@ -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
index 00000000..c38eb398
--- /dev/null
+++ b/dwarflint/readctx.cc
@@ -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
index 00000000..95033ca4
--- /dev/null
+++ b/dwarflint/readctx.hh
@@ -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
index 00000000..ec8232dd
--- /dev/null
+++ b/dwarflint/reloc.cc
@@ -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
index 00000000..eda9fd97
--- /dev/null
+++ b/dwarflint/reloc.hh
@@ -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
index 00000000..a5c857cd
--- /dev/null
+++ b/dwarflint/section_id.cc
@@ -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
index 00000000..2b38f1eb
--- /dev/null
+++ b/dwarflint/section_id.hh
@@ -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
index 00000000..f17ce531
--- /dev/null
+++ b/dwarflint/sections.cc
@@ -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
index 00000000..9e99f2f7
--- /dev/null
+++ b/dwarflint/sections.hh
@@ -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
index 00000000..ef5ec213
--- /dev/null
+++ b/dwarflint/sections_i.hh
@@ -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
index 00000000..2a5690b3
--- /dev/null
+++ b/dwarflint/tests/DW_AT-later-version.bz2
Binary files 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
index 00000000..0221d4e1
--- /dev/null
+++ b/dwarflint/tests/DW_AT_high_pc-below.bz2
Binary files 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
index 00000000..d5196652
--- /dev/null
+++ b/dwarflint/tests/DW_AT_high_pc-relative.bz2
Binary files differ
diff --git a/dwarflint/tests/aranges_terminate_early.bz2 b/dwarflint/tests/aranges_terminate_early.bz2
new file mode 100755
index 00000000..d61925c0
--- /dev/null
+++ b/dwarflint/tests/aranges_terminate_early.bz2
Binary files 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
index 00000000..6fe30488
--- /dev/null
+++ b/dwarflint/tests/check_debug_info_refs-1.bz2
Binary files 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
index 00000000..04749292
--- /dev/null
+++ b/dwarflint/tests/check_debug_info_refs-2.bz2
Binary files 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
index 00000000..1ef81cb8
--- /dev/null
+++ b/dwarflint/tests/check_range_out_of_scope-1.bz2
Binary files differ
diff --git a/dwarflint/tests/check_self_referential_die.bz2 b/dwarflint/tests/check_self_referential_die.bz2
new file mode 100644
index 00000000..06a45305
--- /dev/null
+++ b/dwarflint/tests/check_self_referential_die.bz2
Binary files differ
diff --git a/dwarflint/tests/crc7.ko.debug.bz2 b/dwarflint/tests/crc7.ko.debug.bz2
new file mode 100644
index 00000000..be66966e
--- /dev/null
+++ b/dwarflint/tests/crc7.ko.debug.bz2
Binary files differ
diff --git a/dwarflint/tests/debug_abbrev-duplicate-attribute.bz2 b/dwarflint/tests/debug_abbrev-duplicate-attribute.bz2
new file mode 100644
index 00000000..119c3996
--- /dev/null
+++ b/dwarflint/tests/debug_abbrev-duplicate-attribute.bz2
Binary files differ
diff --git a/dwarflint/tests/empty-1.bz2 b/dwarflint/tests/empty-1.bz2
new file mode 100644
index 00000000..25a7ada0
--- /dev/null
+++ b/dwarflint/tests/empty-1.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-1.bz2 b/dwarflint/tests/garbage-1.bz2
new file mode 100644
index 00000000..de1b26d2
--- /dev/null
+++ b/dwarflint/tests/garbage-1.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-10.bz2 b/dwarflint/tests/garbage-10.bz2
new file mode 100644
index 00000000..2afab22f
--- /dev/null
+++ b/dwarflint/tests/garbage-10.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-11.bz2 b/dwarflint/tests/garbage-11.bz2
new file mode 100644
index 00000000..450e72cc
--- /dev/null
+++ b/dwarflint/tests/garbage-11.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-12.bz2 b/dwarflint/tests/garbage-12.bz2
new file mode 100644
index 00000000..a38e928e
--- /dev/null
+++ b/dwarflint/tests/garbage-12.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-2.bz2 b/dwarflint/tests/garbage-2.bz2
new file mode 100644
index 00000000..3cd56192
--- /dev/null
+++ b/dwarflint/tests/garbage-2.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-3.bz2 b/dwarflint/tests/garbage-3.bz2
new file mode 100644
index 00000000..ad66a414
--- /dev/null
+++ b/dwarflint/tests/garbage-3.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-4.bz2 b/dwarflint/tests/garbage-4.bz2
new file mode 100644
index 00000000..20e75d88
--- /dev/null
+++ b/dwarflint/tests/garbage-4.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-5.bz2 b/dwarflint/tests/garbage-5.bz2
new file mode 100644
index 00000000..9b0401eb
--- /dev/null
+++ b/dwarflint/tests/garbage-5.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-6.bz2 b/dwarflint/tests/garbage-6.bz2
new file mode 100644
index 00000000..6cb8a44b
--- /dev/null
+++ b/dwarflint/tests/garbage-6.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-7.bz2 b/dwarflint/tests/garbage-7.bz2
new file mode 100644
index 00000000..41963b62
--- /dev/null
+++ b/dwarflint/tests/garbage-7.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-8.bz2 b/dwarflint/tests/garbage-8.bz2
new file mode 100644
index 00000000..b9889832
--- /dev/null
+++ b/dwarflint/tests/garbage-8.bz2
Binary files differ
diff --git a/dwarflint/tests/garbage-9.bz2 b/dwarflint/tests/garbage-9.bz2
new file mode 100644
index 00000000..c2e7a8bf
--- /dev/null
+++ b/dwarflint/tests/garbage-9.bz2
Binary files differ
diff --git a/dwarflint/tests/hello.bad-1.bz2 b/dwarflint/tests/hello.bad-1.bz2
new file mode 100644
index 00000000..52531fff
--- /dev/null
+++ b/dwarflint/tests/hello.bad-1.bz2
Binary files differ
diff --git a/dwarflint/tests/hello.bad-1.s b/dwarflint/tests/hello.bad-1.s
new file mode 100644
index 00000000..8679ec10
--- /dev/null
+++ b/dwarflint/tests/hello.bad-1.s
@@ -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
index 00000000..8ccbe37b
--- /dev/null
+++ b/dwarflint/tests/hello.bad-2.bz2
Binary files differ
diff --git a/dwarflint/tests/hello.bad-2.s b/dwarflint/tests/hello.bad-2.s
new file mode 100644
index 00000000..5fcd2d50
--- /dev/null
+++ b/dwarflint/tests/hello.bad-2.s
@@ -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
index 00000000..35b73e76
--- /dev/null
+++ b/dwarflint/tests/hello.bad-3.bz2
Binary files differ
diff --git a/dwarflint/tests/hello.bad-3.s b/dwarflint/tests/hello.bad-3.s
new file mode 100644
index 00000000..5f0041b7
--- /dev/null
+++ b/dwarflint/tests/hello.bad-3.s
@@ -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
index 00000000..356520ea
--- /dev/null
+++ b/dwarflint/tests/libdl-2.12.so.debug.bz2
Binary files differ
diff --git a/dwarflint/tests/location-leaks.bz2 b/dwarflint/tests/location-leaks.bz2
new file mode 100644
index 00000000..ccf6532e
--- /dev/null
+++ b/dwarflint/tests/location-leaks.bz2
Binary files differ
diff --git a/dwarflint/tests/nodebug.bz2 b/dwarflint/tests/nodebug.bz2
new file mode 100755
index 00000000..22320a45
--- /dev/null
+++ b/dwarflint/tests/nodebug.bz2
Binary files differ
diff --git a/dwarflint/tests/null.o.bz2 b/dwarflint/tests/null.o.bz2
new file mode 100644
index 00000000..f272d5a7
--- /dev/null
+++ b/dwarflint/tests/null.o.bz2
Binary files 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
index 00000000..d662502b
--- /dev/null
+++ b/dwarflint/tests/run-DW_AT-later-version.sh
@@ -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
index 00000000..4b4d6eac
--- /dev/null
+++ b/dwarflint/tests/run-DW_AT_high_pc-below.sh
@@ -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
index 00000000..8757ba0e
--- /dev/null
+++ b/dwarflint/tests/run-DW_AT_high_pc-relative.sh
@@ -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
index 00000000..43b2ec2c
--- /dev/null
+++ b/dwarflint/tests/run-aranges_terminate_early.sh
@@ -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
index 00000000..ebe3873a
--- /dev/null
+++ b/dwarflint/tests/run-bad.sh
@@ -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
index 00000000..4e790b88
--- /dev/null
+++ b/dwarflint/tests/run-check_debug_info_refs.sh
@@ -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
index 00000000..c5622872
--- /dev/null
+++ b/dwarflint/tests/run-check_duplicate_DW_tag_variable.sh
@@ -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
index 00000000..fa3b2de2
--- /dev/null
+++ b/dwarflint/tests/run-check_range_out_of_scope.sh
@@ -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
index 00000000..7fe83c63
--- /dev/null
+++ b/dwarflint/tests/run-check_self_referential_die.sh
@@ -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
index 00000000..b189bee4
--- /dev/null
+++ b/dwarflint/tests/run-debug_abbrev-duplicate-attribute.sh
@@ -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
index 00000000..f9f33635
--- /dev/null
+++ b/dwarflint/tests/run-libdl-2.12.so.debug.sh
@@ -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
index 00000000..a8eb63f9
--- /dev/null
+++ b/dwarflint/tests/run-location-leaks.sh
@@ -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
index 00000000..1b67a706
--- /dev/null
+++ b/dwarflint/tests/run-nodebug.sh
@@ -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
index 00000000..fdcf63fd
--- /dev/null
+++ b/dwarflint/tests/run-test-all-dies-it.sh
@@ -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
index 00000000..d1ed96ac
--- /dev/null
+++ b/dwarflint/tests/run-upper.sh
@@ -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
index 00000000..ec242974
--- /dev/null
+++ b/dwarflint/tests/test-all-dies-it.cc
@@ -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
index 00000000..3d3add0a
--- /dev/null
+++ b/dwarflint/tests/test-coverage.cc
@@ -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
index 00000000..57bfda2c
--- /dev/null
+++ b/dwarflint/tests/test-wrap.cc
@@ -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
index 00000000..88448869
--- /dev/null
+++ b/dwarflint/tests/upper.bz2
Binary files differ
diff --git a/dwarflint/wrap.cc b/dwarflint/wrap.cc
new file mode 100644
index 00000000..6d40ee02
--- /dev/null
+++ b/dwarflint/wrap.cc
@@ -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
index 00000000..d9e052bd
--- /dev/null
+++ b/dwarflint/wrap.hh
@@ -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