From c926fd82bbd336b317266d43b9fa67a83397b06b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 13 Dec 2019 20:04:47 +0000 Subject: [PATCH] libbacktrace: add DWARF 5 support * dwarf.c (struct attr): Add val field. (enum attr_val_encoding): Add ATTR_VAL_ADDDRESS_INDEX, ATTR_VAL_STRING_INDEX, ATTR_VAL_RNGLISTS_INDEX. (struct line_header): Add addrsize field. (struct line_header_format): Define. (struct unit): Add str_offsets_base, addr_base, and rnglists_base fields. (read_uint24): New static function. (read_attribute): Add implicit_val parameter. Replace dwarf_str and dwarf_str_size parameters with dwarf_sections parameter. Add support for new DWARF 5 forms. Change all callers. (resolve_string): New static function. (resolve_addr_index): Likewise. (read_abbrevs): Support DW_FORM_implicit_const. (struct pcrange): Add lowpc_is_addr_index, highpc_is_addr_Index, and ranges_is_index fields. (update_pcrange): Support DWARF 5 encodings. (add_high_low_range): New static function, split out of add_ranges. (add_ranges_from_ranges): Likewise. (add_ranges_from_rnglists): New static function. (add_ranges): Just call new helper functions. (find_address_ranges): Use resolve_string for strings, after reading all attributes. Handle new DWARF 5 attributes. (build_address_map): Support DWARF 5 compilation units. (read_v2_paths): New static function, split out of read_line_header. (read_lnct): New static function. (read_line_header_format_entries): Likewise. (read_line_header): Add ddata parameter. Support DWARF 5 line headers. Call new helper functions. Change all callers. (read_line_program): Use addrsize from line program header. Don't special case directory index 0 for DWARF 5. (read_referenced_name): Use resolve_string. (read_function_entry): Handle DWARF 5 encodings. Use resolve_string. * internal.h (enum dwarf_section): Add DEBUG_ADDR, DEBUG_STR_OFFSETS, DEBUG_LINE_STR, DEBUG_RNGLISTS. * elf.c (dwarf_section_names): Add new section names. * pecoff.c (dwarf_section_names): Likewise. * xcoff.c (xcoff_add): Clear dwarf_sections before setting fields. * configure.ac: Define HAVE_DWARF5 automake conditional. * Makefile.am (dwarf5_SOURCES): New variable if HAVE_DWARF5. (dwarf5_CFLAGS, dwarf5_LDADD): Likewise. (dwarf5_alloc_SOURCES, dwarf5_alloc_CFLAGS): Likewise. (dwarf5_alloc_LDADD): Likewise. (BUILDTESTS): Add dwarf5 tests if HAVE_DWARF5. (CLEANFILES, clean-local): Define. From-SVN: r279380 --- libbacktrace/ChangeLog | 53 ++ libbacktrace/Makefile.am | 21 + libbacktrace/Makefile.in | 112 +++- libbacktrace/configure | 40 +- libbacktrace/configure.ac | 11 + libbacktrace/dwarf.c | 1241 +++++++++++++++++++++++++++++++------ libbacktrace/elf.c | 4 + libbacktrace/internal.h | 4 + libbacktrace/pecoff.c | 6 +- libbacktrace/xcoff.c | 2 + 10 files changed, 1270 insertions(+), 224 deletions(-) diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index 7d91aadb818..b63d216c8c4 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,56 @@ +2019-12-13 Ian Lance Taylor + + Add DWARF 5 support. + * dwarf.c (struct attr): Add val field. + (enum attr_val_encoding): Add ATTR_VAL_ADDDRESS_INDEX, + ATTR_VAL_STRING_INDEX, ATTR_VAL_RNGLISTS_INDEX. + (struct line_header): Add addrsize field. + (struct line_header_format): Define. + (struct unit): Add str_offsets_base, addr_base, and rnglists_base + fields. + (read_uint24): New static function. + (read_attribute): Add implicit_val parameter. Replace dwarf_str + and dwarf_str_size parameters with dwarf_sections parameter. Add + support for new DWARF 5 forms. Change all callers. + (resolve_string): New static function. + (resolve_addr_index): Likewise. + (read_abbrevs): Support DW_FORM_implicit_const. + (struct pcrange): Add lowpc_is_addr_index, highpc_is_addr_Index, + and ranges_is_index fields. + (update_pcrange): Support DWARF 5 encodings. + (add_high_low_range): New static function, split out of + add_ranges. + (add_ranges_from_ranges): Likewise. + (add_ranges_from_rnglists): New static function. + (add_ranges): Just call new helper functions. + (find_address_ranges): Use resolve_string for strings, after + reading all attributes. Handle new DWARF 5 attributes. + (build_address_map): Support DWARF 5 compilation units. + (read_v2_paths): New static function, split out of + read_line_header. + (read_lnct): New static function. + (read_line_header_format_entries): Likewise. + (read_line_header): Add ddata parameter. Support DWARF 5 line + headers. Call new helper functions. Change all callers. + (read_line_program): Use addrsize from line program header. Don't + special case directory index 0 for DWARF 5. + (read_referenced_name): Use resolve_string. + (read_function_entry): Handle DWARF 5 encodings. Use + resolve_string. + * internal.h (enum dwarf_section): Add DEBUG_ADDR, + DEBUG_STR_OFFSETS, DEBUG_LINE_STR, DEBUG_RNGLISTS. + * elf.c (dwarf_section_names): Add new section names. + * pecoff.c (dwarf_section_names): Likewise. + * xcoff.c (xcoff_add): Clear dwarf_sections before setting + fields. + * configure.ac: Define HAVE_DWARF5 automake conditional. + * Makefile.am (dwarf5_SOURCES): New variable if HAVE_DWARF5. + (dwarf5_CFLAGS, dwarf5_LDADD): Likewise. + (dwarf5_alloc_SOURCES, dwarf5_alloc_CFLAGS): Likewise. + (dwarf5_alloc_LDADD): Likewise. + (BUILDTESTS): Add dwarf5 tests if HAVE_DWARF5. + (CLEANFILES, clean-local): Define. + 2019-12-08 Ian Lance Taylor * dwarf.c (struct pcrange): Define. diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index e989d8e9f6c..79abcc9174b 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -385,12 +385,33 @@ BUILDTESTS += ctestg_alloc ctesta_alloc endif +if HAVE_DWARF5 + +dwarf5_SOURCES = btest.c testlib.c +dwarf5_CFLAGS = $(AM_CFLAGS) -gdwarf-5 +dwarf5_LDADD = libbacktrace.la + +BUILDTESTS += dwarf5 + +dwarf5_alloc_SOURCES = $(dwarf5_SOURCES) +dwarf5_alloc_CFLAGS = $(dwarf5_CFLAGS) +dwarf5_alloc_LDADD = libbacktrace_alloc.la + +BUILDTESTS += dwarf5_alloc + +endif + endif NATIVE check_PROGRAMS += $(BUILDTESTS) TESTS += $(BUILDTESTS) +CLEANFILES = $(TESTS) *.debug elf_for_test.c edtest2_build.c gen_edtest2_build + +clean-local: + -rm -rf usr + # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking # with GCC bootstrap will cause some of the objects to depend on diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index 4ade8e9efcf..01855bfac07 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -121,10 +121,10 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ - $(am__EXEEXT_11) + $(am__EXEEXT_12) TESTS = $(am__append_4) $(am__append_6) $(am__append_8) \ $(am__append_11) $(am__append_12) $(am__append_18) \ - $(am__EXEEXT_11) + $(am__EXEEXT_12) @HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_1 = libbacktrace_elf_for_test.la @NATIVE_TRUE@am__append_2 = test_elf_32 test_elf_64 test_xcoff_32 \ @NATIVE_TRUE@ test_xcoff_64 test_pecoff test_unknown unittest \ @@ -148,6 +148,7 @@ TESTS = $(am__append_4) $(am__append_6) $(am__append_8) \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__append_19 = ctestg ctesta \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg_alloc \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta_alloc +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am__append_20 = dwarf5 dwarf5_alloc subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/cet.m4 \ @@ -224,9 +225,11 @@ libbacktrace_noformat_la_OBJECTS = \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta$(EXEEXT) \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg_alloc$(EXEEXT) \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta_alloc$(EXEEXT) -am__EXEEXT_11 = $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am__EXEEXT_11 = dwarf5$(EXEEXT) \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ dwarf5_alloc$(EXEEXT) +am__EXEEXT_12 = $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \ $(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_9) \ - $(am__EXEEXT_10) + $(am__EXEEXT_10) $(am__EXEEXT_11) @NATIVE_TRUE@am_allocfail_OBJECTS = allocfail.$(OBJEXT) \ @NATIVE_TRUE@ testlib.$(OBJEXT) allocfail_OBJECTS = $(am_allocfail_OBJECTS) @@ -305,20 +308,39 @@ ctestg_alloc_OBJECTS = $(am_ctestg_alloc_OBJECTS) ctestg_alloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ctestg_alloc_CFLAGS) \ $(CFLAGS) $(ctestg_alloc_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am_dwarf5_OBJECTS = \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ dwarf5-btest.$(OBJEXT) \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ dwarf5-testlib.$(OBJEXT) +dwarf5_OBJECTS = $(am_dwarf5_OBJECTS) +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_DEPENDENCIES = libbacktrace.la +dwarf5_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(dwarf5_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am__objects_7 = \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ dwarf5_alloc-btest.$(OBJEXT) \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ dwarf5_alloc-testlib.$(OBJEXT) +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am_dwarf5_alloc_OBJECTS = \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ $(am__objects_7) +dwarf5_alloc_OBJECTS = $(am_dwarf5_alloc_OBJECTS) +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_alloc_DEPENDENCIES = \ +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ libbacktrace_alloc.la +dwarf5_alloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(dwarf5_alloc_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ @NATIVE_TRUE@am_edtest_OBJECTS = edtest.$(OBJEXT) \ @NATIVE_TRUE@ edtest2_build.$(OBJEXT) testlib.$(OBJEXT) edtest_OBJECTS = $(am_edtest_OBJECTS) @NATIVE_TRUE@edtest_DEPENDENCIES = libbacktrace.la -@NATIVE_TRUE@am__objects_7 = edtest.$(OBJEXT) edtest2_build.$(OBJEXT) \ +@NATIVE_TRUE@am__objects_8 = edtest.$(OBJEXT) edtest2_build.$(OBJEXT) \ @NATIVE_TRUE@ testlib.$(OBJEXT) -@NATIVE_TRUE@am_edtest_alloc_OBJECTS = $(am__objects_7) +@NATIVE_TRUE@am_edtest_alloc_OBJECTS = $(am__objects_8) edtest_alloc_OBJECTS = $(am_edtest_alloc_OBJECTS) @NATIVE_TRUE@edtest_alloc_DEPENDENCIES = libbacktrace_alloc.la @NATIVE_TRUE@am_stest_OBJECTS = stest.$(OBJEXT) stest_OBJECTS = $(am_stest_OBJECTS) @NATIVE_TRUE@stest_DEPENDENCIES = libbacktrace.la -@NATIVE_TRUE@am__objects_8 = stest.$(OBJEXT) -@NATIVE_TRUE@am_stest_alloc_OBJECTS = $(am__objects_8) +@NATIVE_TRUE@am__objects_9 = stest.$(OBJEXT) +@NATIVE_TRUE@am_stest_alloc_OBJECTS = $(am__objects_9) stest_alloc_OBJECTS = $(am_stest_alloc_OBJECTS) @NATIVE_TRUE@stest_alloc_DEPENDENCIES = libbacktrace_alloc.la @NATIVE_TRUE@am_test_elf_32_OBJECTS = test_format.$(OBJEXT) \ @@ -359,11 +381,11 @@ ttest_OBJECTS = $(am_ttest_OBJECTS) ttest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ttest_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__objects_9 = \ +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__objects_10 = \ @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ ttest_alloc-ttest.$(OBJEXT) \ @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ ttest_alloc-testlib.$(OBJEXT) @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am_ttest_alloc_OBJECTS = \ -@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ $(am__objects_9) +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ $(am__objects_10) ttest_alloc_OBJECTS = $(am_ttest_alloc_OBJECTS) @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ttest_alloc_DEPENDENCIES = \ @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ libbacktrace_alloc.la @@ -374,8 +396,8 @@ ttest_alloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ @NATIVE_TRUE@ testlib.$(OBJEXT) unittest_OBJECTS = $(am_unittest_OBJECTS) @NATIVE_TRUE@unittest_DEPENDENCIES = libbacktrace.la -@NATIVE_TRUE@am__objects_10 = unittest.$(OBJEXT) testlib.$(OBJEXT) -@NATIVE_TRUE@am_unittest_alloc_OBJECTS = $(am__objects_10) +@NATIVE_TRUE@am__objects_11 = unittest.$(OBJEXT) testlib.$(OBJEXT) +@NATIVE_TRUE@am_unittest_alloc_OBJECTS = $(am__objects_11) unittest_alloc_OBJECTS = $(am_unittest_alloc_OBJECTS) @NATIVE_TRUE@unittest_alloc_DEPENDENCIES = libbacktrace_alloc.la @HAVE_ELF_TRUE@@NATIVE_TRUE@am_ztest_OBJECTS = ztest-ztest.$(OBJEXT) \ @@ -387,11 +409,11 @@ ztest_OBJECTS = $(am_ztest_OBJECTS) ztest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ztest_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -@HAVE_ELF_TRUE@@NATIVE_TRUE@am__objects_11 = \ +@HAVE_ELF_TRUE@@NATIVE_TRUE@am__objects_12 = \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ ztest_alloc-ztest.$(OBJEXT) \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ ztest_alloc-testlib.$(OBJEXT) @HAVE_ELF_TRUE@@NATIVE_TRUE@am_ztest_alloc_OBJECTS = \ -@HAVE_ELF_TRUE@@NATIVE_TRUE@ $(am__objects_11) +@HAVE_ELF_TRUE@@NATIVE_TRUE@ $(am__objects_12) ztest_alloc_OBJECTS = $(am_ztest_alloc_OBJECTS) @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_alloc_DEPENDENCIES = \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ libbacktrace_alloc.la \ @@ -441,7 +463,8 @@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ $(b2test_SOURCES) $(b3test_SOURCES) $(btest_SOURCES) \ $(btest_alloc_SOURCES) $(btest_lto_SOURCES) $(ctesta_SOURCES) \ $(ctesta_alloc_SOURCES) $(ctestg_SOURCES) \ - $(ctestg_alloc_SOURCES) $(edtest_SOURCES) \ + $(ctestg_alloc_SOURCES) $(dwarf5_SOURCES) \ + $(dwarf5_alloc_SOURCES) $(edtest_SOURCES) \ $(edtest_alloc_SOURCES) $(stest_SOURCES) \ $(stest_alloc_SOURCES) $(test_elf_32_SOURCES) \ $(test_elf_64_SOURCES) $(test_pecoff_SOURCES) \ @@ -863,7 +886,7 @@ libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) # Add test to this variable, if you want it to be build and run. BUILDTESTS = $(am__append_2) $(am__append_9) $(am__append_10) \ $(am__append_15) $(am__append_16) $(am__append_17) \ - $(am__append_19) + $(am__append_19) $(am__append_20) @NATIVE_TRUE@check_LTLIBRARIES = libbacktrace_alloc.la \ @NATIVE_TRUE@ libbacktrace_noformat.la $(am__append_1) \ @NATIVE_TRUE@ libbacktrace_instrumented_alloc.la @@ -960,6 +983,13 @@ BUILDTESTS = $(am__append_2) $(am__append_9) $(am__append_10) \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_alloc_CFLAGS = $(ctesta_CFLAGS) @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_alloc_LDFLAGS = $(ctesta_LDFLAGS) @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ctesta_alloc_LDADD = libbacktrace_alloc.la +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_SOURCES = btest.c testlib.c +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_CFLAGS = $(AM_CFLAGS) -gdwarf-5 +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_LDADD = libbacktrace.la +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_alloc_SOURCES = $(dwarf5_SOURCES) +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_alloc_CFLAGS = $(dwarf5_CFLAGS) +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@dwarf5_alloc_LDADD = libbacktrace_alloc.la +CLEANFILES = $(TESTS) *.debug elf_for_test.c edtest2_build.c gen_edtest2_build # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking @@ -1124,6 +1154,14 @@ ctestg_alloc$(EXEEXT): $(ctestg_alloc_OBJECTS) $(ctestg_alloc_DEPENDENCIES) $(EX @rm -f ctestg_alloc$(EXEEXT) $(AM_V_CCLD)$(ctestg_alloc_LINK) $(ctestg_alloc_OBJECTS) $(ctestg_alloc_LDADD) $(LIBS) +dwarf5$(EXEEXT): $(dwarf5_OBJECTS) $(dwarf5_DEPENDENCIES) $(EXTRA_dwarf5_DEPENDENCIES) + @rm -f dwarf5$(EXEEXT) + $(AM_V_CCLD)$(dwarf5_LINK) $(dwarf5_OBJECTS) $(dwarf5_LDADD) $(LIBS) + +dwarf5_alloc$(EXEEXT): $(dwarf5_alloc_OBJECTS) $(dwarf5_alloc_DEPENDENCIES) $(EXTRA_dwarf5_alloc_DEPENDENCIES) + @rm -f dwarf5_alloc$(EXEEXT) + $(AM_V_CCLD)$(dwarf5_alloc_LINK) $(dwarf5_alloc_OBJECTS) $(dwarf5_alloc_LDADD) $(LIBS) + edtest$(EXEEXT): $(edtest_OBJECTS) $(edtest_DEPENDENCIES) $(EXTRA_edtest_DEPENDENCIES) @rm -f edtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(edtest_OBJECTS) $(edtest_LDADD) $(LIBS) @@ -1311,6 +1349,30 @@ ctestg_alloc-testlib.o: testlib.c ctestg_alloc-testlib.obj: testlib.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ctestg_alloc_CFLAGS) $(CFLAGS) -c -o ctestg_alloc-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` +dwarf5-btest.o: btest.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_CFLAGS) $(CFLAGS) -c -o dwarf5-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c + +dwarf5-btest.obj: btest.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_CFLAGS) $(CFLAGS) -c -o dwarf5-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` + +dwarf5-testlib.o: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_CFLAGS) $(CFLAGS) -c -o dwarf5-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +dwarf5-testlib.obj: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_CFLAGS) $(CFLAGS) -c -o dwarf5-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + +dwarf5_alloc-btest.o: btest.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_alloc_CFLAGS) $(CFLAGS) -c -o dwarf5_alloc-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c + +dwarf5_alloc-btest.obj: btest.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_alloc_CFLAGS) $(CFLAGS) -c -o dwarf5_alloc-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` + +dwarf5_alloc-testlib.o: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_alloc_CFLAGS) $(CFLAGS) -c -o dwarf5_alloc-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +dwarf5_alloc-testlib.obj: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dwarf5_alloc_CFLAGS) $(CFLAGS) -c -o dwarf5_alloc-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + ttest-ttest.o: ttest.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ttest_CFLAGS) $(CFLAGS) -c -o ttest-ttest.o `test -f 'ttest.c' || echo '$(srcdir)/'`ttest.c @@ -1771,6 +1833,20 @@ ctesta_alloc.log: ctesta_alloc$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +dwarf5.log: dwarf5$(EXEEXT) + @p='dwarf5$(EXEEXT)'; \ + b='dwarf5'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dwarf5_alloc.log: dwarf5_alloc$(EXEEXT) + @p='dwarf5_alloc$(EXEEXT)'; \ + b='dwarf5_alloc'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ @@ -1816,6 +1892,7 @@ mostlyclean-generic: -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) @@ -1973,6 +2050,9 @@ uninstall-am: @NATIVE_TRUE@ "$(TEST_BUILD_ID_DIR)" \ @NATIVE_TRUE@ $< @NATIVE_TRUE@ $(OBJCOPY) --strip-debug $< $@ + +clean-local: + -rm -rf usr alloc.lo: config.h backtrace.h internal.h backtrace.lo: config.h backtrace.h internal.h btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h diff --git a/libbacktrace/configure b/libbacktrace/configure index 03c6d2ef58b..676adb89224 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -643,6 +643,8 @@ HAVE_COMPRESSED_DEBUG_FALSE HAVE_COMPRESSED_DEBUG_TRUE HAVE_ZLIB_FALSE HAVE_ZLIB_TRUE +HAVE_DWARF5_FALSE +HAVE_DWARF5_TRUE HAVE_PTHREAD_FALSE HAVE_PTHREAD_TRUE PTHREAD_CFLAGS @@ -11497,7 +11499,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11500 "configure" +#line 11502 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11603,7 +11605,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11606 "configure" +#line 11608 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -13280,6 +13282,36 @@ else fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -gdwarf-5 is supported" >&5 +$as_echo_n "checking whether -gdwarf-5 is supported... " >&6; } +if ${libbacktrace_cv_lib_dwarf5+:} false; then : + $as_echo_n "(cached) " >&6 +else + CFLAGS_hold=$CFLAGS +CFLAGS="$CFLAGS -gdwarf-5" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int i; +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + libbacktrace_cv_lib_dwarf5=yes +else + libbacktrace_cv_lib_dwarf5=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +CFLAGS=$CFLAGS_hold +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libbacktrace_cv_lib_dwarf5" >&5 +$as_echo "$libbacktrace_cv_lib_dwarf5" >&6; } + if test "$libbacktrace_cv_lib_dwarf5" = yes; then + HAVE_DWARF5_TRUE= + HAVE_DWARF5_FALSE='#' +else + HAVE_DWARF5_TRUE='#' + HAVE_DWARF5_FALSE= +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5 $as_echo_n "checking for compress in -lz... " >&6; } if ${ac_cv_lib_z_compress+:} false; then : @@ -13665,6 +13697,10 @@ if test -z "${HAVE_PTHREAD_TRUE}" && test -z "${HAVE_PTHREAD_FALSE}"; then as_fn_error $? "conditional \"HAVE_PTHREAD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_DWARF5_TRUE}" && test -z "${HAVE_DWARF5_FALSE}"; then + as_fn_error $? "conditional \"HAVE_DWARF5\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${HAVE_ZLIB_TRUE}" && test -z "${HAVE_ZLIB_FALSE}"; then as_fn_error $? "conditional \"HAVE_ZLIB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index a657ef6097a..cda4f09f72e 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -420,6 +420,17 @@ AC_SUBST(PTHREAD_CFLAGS) AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes) +dnl Test whether the compiler supports the -gdwarf-5 option. +AC_CACHE_CHECK([whether -gdwarf-5 is supported], +[libbacktrace_cv_lib_dwarf5], +[CFLAGS_hold=$CFLAGS +CFLAGS="$CFLAGS -gdwarf-5" +AC_COMPILE_IFELSE([AC_LANG_SOURCE([int i;])], +[libbacktrace_cv_lib_dwarf5=yes], +[libbacktrace_cv_lib_dwarf5=no]) +CFLAGS=$CFLAGS_hold]) +AM_CONDITIONAL(HAVE_DWARF5, test "$libbacktrace_cv_lib_dwarf5" = yes) + AC_CHECK_LIB([z], [compress], [AC_DEFINE(HAVE_ZLIB, 1, [Define if -lz is available.])]) AM_CONDITIONAL(HAVE_ZLIB, test "$ac_cv_lib_z_compress" = yes) diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c index 9e00792da50..d27376860a5 100644 --- a/libbacktrace/dwarf.c +++ b/libbacktrace/dwarf.c @@ -92,6 +92,8 @@ struct attr enum dwarf_attribute name; /* The attribute form. */ enum dwarf_form form; + /* The attribute value, for DW_FORM_implicit_const. */ + int64_t val; }; /* A single DWARF abbreviation. */ @@ -133,22 +135,29 @@ enum attr_val_encoding ATTR_VAL_NONE, /* An address. */ ATTR_VAL_ADDRESS, + /* An index into the .debug_addr section, whose value is relative to + * the DW_AT_addr_base attribute of the compilation unit. */ + ATTR_VAL_ADDRESS_INDEX, /* A unsigned integer. */ ATTR_VAL_UINT, /* A sigd integer. */ ATTR_VAL_SINT, /* A string. */ ATTR_VAL_STRING, + /* An index into the .debug_str_offsets section. */ + ATTR_VAL_STRING_INDEX, /* An offset to other data in the containing unit. */ ATTR_VAL_REF_UNIT, - /* An offset to other data within the .dwarf_info section. */ + /* An offset to other data within the .debug_info section. */ ATTR_VAL_REF_INFO, - /* An offset to other data within the alt .dwarf_info section. */ + /* An offset to other data within the alt .debug_info section. */ ATTR_VAL_REF_ALT_INFO, /* An offset to data in some other section. */ ATTR_VAL_REF_SECTION, /* A type signature. */ ATTR_VAL_REF_TYPE, + /* An index into the .debug_rnglists section. */ + ATTR_VAL_RNGLISTS_INDEX, /* A block of data (not represented). */ ATTR_VAL_BLOCK, /* An expression (not represented). */ @@ -163,7 +172,7 @@ struct attr_val enum attr_val_encoding encoding; union { - /* ATTR_VAL_ADDRESS, ATTR_VAL_UINT, ATTR_VAL_REF*. */ + /* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*. */ uint64_t uint; /* ATTR_VAL_SINT. */ int64_t sint; @@ -179,6 +188,8 @@ struct line_header { /* The version of the line number information. */ int version; + /* Address size. */ + int addrsize; /* The minimum instruction length. */ unsigned int min_insn_len; /* The maximum number of ops per instruction. */ @@ -201,6 +212,14 @@ struct line_header const char **filenames; }; +/* A format description from a line header. */ + +struct line_header_format +{ + int lnct; /* LNCT code. */ + enum dwarf_form form; /* Form of entry data. */ +}; + /* Map a single PC value to a file/line. We will keep a vector of these sorted by PC value. Each file/line will be correct from the PC up to the PC of the next entry if there is one. We allocate one @@ -297,6 +316,12 @@ struct unit int addrsize; /* Offset into line number information. */ off_t lineoff; + /* Offset of compilation unit in .debug_str_offsets. */ + uint64_t str_offsets_base; + /* Offset of compilation unit in .debug_addr. */ + uint64_t addr_base; + /* Offset of compilation unit in .debug_rnglists. */ + uint64_t rnglists_base; /* Primary source file. */ const char *filename; /* Compilation command working directory. */ @@ -483,6 +508,23 @@ read_uint16 (struct dwarf_buf *buf) return ((uint16_t) p[1] << 8) | (uint16_t) p[0]; } +/* Read a 24 bit value from BUF and advance 3 bytes. */ + +static uint32_t +read_uint24 (struct dwarf_buf *buf) +{ + const unsigned char *p = buf->buf; + + if (!advance (buf, 3)) + return 0; + if (buf->is_bigendian) + return (((uint32_t) p[0] << 16) | ((uint32_t) p[1] << 8) + | (uint32_t) p[2]); + else + return (((uint32_t) p[2] << 16) | ((uint32_t) p[1] << 8) + | (uint32_t) p[0]); +} + /* Read a uint32 from BUF and advance 4 bytes. */ static uint32_t @@ -709,9 +751,9 @@ free_abbrevs (struct backtrace_state *state, struct abbrevs *abbrevs, forms, because we don't care about them. */ static int -read_attribute (enum dwarf_form form, struct dwarf_buf *buf, - int is_dwarf64, int version, int addrsize, - const unsigned char *dwarf_str, size_t dwarf_str_size, +read_attribute (enum dwarf_form form, uint64_t implicit_val, + struct dwarf_buf *buf, int is_dwarf64, int version, + int addrsize, const struct dwarf_sections *dwarf_sections, struct dwarf_data *altlink, struct attr_val *val) { /* Avoid warnings about val.u.FIELD may be used uninitialized if @@ -744,6 +786,9 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, val->encoding = ATTR_VAL_UINT; val->u.uint = read_uint64 (buf); return 1; + case DW_FORM_data16: + val->encoding = ATTR_VAL_BLOCK; + return advance (buf, 16); case DW_FORM_string: val->encoding = ATTR_VAL_STRING; val->u.string = read_string (buf); @@ -771,13 +816,29 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, uint64_t offset; offset = read_offset (buf, is_dwarf64); - if (offset >= dwarf_str_size) + if (offset >= dwarf_sections->size[DEBUG_STR]) { dwarf_buf_error (buf, "DW_FORM_strp out of range"); return 0; } val->encoding = ATTR_VAL_STRING; - val->u.string = (const char *) dwarf_str + offset; + val->u.string = + (const char *) dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + case DW_FORM_line_strp: + { + uint64_t offset; + + offset = read_offset (buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_LINE_STR]) + { + dwarf_buf_error (buf, "DW_FORM_line_strp out of range"); + return 0; + } + val->encoding = ATTR_VAL_STRING; + val->u.string = + (const char *) dwarf_sections->data[DEBUG_LINE_STR] + offset; return 1; } case DW_FORM_udata: @@ -816,9 +877,15 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, uint64_t form; form = read_uleb128 (buf); - return read_attribute ((enum dwarf_form) form, buf, is_dwarf64, - version, addrsize, dwarf_str, dwarf_str_size, - altlink, val); + if (form == DW_FORM_implicit_const) + { + dwarf_buf_error (buf, + "DW_FORM_indirect to DW_FORM_implicit_const"); + return 0; + } + return read_attribute ((enum dwarf_form) form, 0, buf, is_dwarf64, + version, addrsize, dwarf_sections, altlink, + val); } case DW_FORM_sec_offset: val->encoding = ATTR_VAL_REF_SECTION; @@ -835,6 +902,88 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, val->encoding = ATTR_VAL_REF_TYPE; val->u.uint = read_uint64 (buf); return 1; + case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2: + case DW_FORM_strx3: case DW_FORM_strx4: + { + uint64_t offset; + + switch (form) + { + case DW_FORM_strx: + offset = read_uleb128 (buf); + break; + case DW_FORM_strx1: + offset = read_byte (buf); + break; + case DW_FORM_strx2: + offset = read_uint16 (buf); + break; + case DW_FORM_strx3: + offset = read_uint24 (buf); + break; + case DW_FORM_strx4: + offset = read_uint32 (buf); + break; + default: + /* This case can't happen. */ + return 0; + } + val->encoding = ATTR_VAL_STRING_INDEX; + val->u.uint = offset; + return 1; + } + case DW_FORM_addrx: case DW_FORM_addrx1: case DW_FORM_addrx2: + case DW_FORM_addrx3: case DW_FORM_addrx4: + { + uint64_t offset; + + switch (form) + { + case DW_FORM_addrx: + offset = read_uleb128 (buf); + break; + case DW_FORM_addrx1: + offset = read_byte (buf); + break; + case DW_FORM_addrx2: + offset = read_uint16 (buf); + break; + case DW_FORM_addrx3: + offset = read_uint24 (buf); + break; + case DW_FORM_addrx4: + offset = read_uint32 (buf); + break; + default: + /* This case can't happen. */ + return 0; + } + val->encoding = ATTR_VAL_ADDRESS_INDEX; + val->u.uint = offset; + return 1; + } + case DW_FORM_ref_sup4: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uint32 (buf); + return 1; + case DW_FORM_ref_sup8: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uint64 (buf); + return 1; + case DW_FORM_implicit_const: + val->encoding = ATTR_VAL_UINT; + val->u.uint = implicit_val; + return 1; + case DW_FORM_loclistx: + /* We don't distinguish this from DW_FORM_sec_offset. It + * shouldn't matter since we don't care about loclists. */ + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uleb128 (buf); + return 1; + case DW_FORM_rnglistx: + val->encoding = ATTR_VAL_RNGLISTS_INDEX; + val->u.uint = read_uleb128 (buf); + return 1; case DW_FORM_GNU_addr_index: val->encoding = ATTR_VAL_REF_SECTION; val->u.uint = read_uleb128 (buf); @@ -852,9 +1001,10 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, } val->encoding = ATTR_VAL_REF_ALT_INFO; return 1; - case DW_FORM_GNU_strp_alt: + case DW_FORM_strp_sup: case DW_FORM_GNU_strp_alt: { uint64_t offset; + offset = read_offset (buf, is_dwarf64); if (altlink == NULL) { @@ -863,7 +1013,7 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, } if (offset >= altlink->dwarf_sections.size[DEBUG_STR]) { - dwarf_buf_error (buf, "DW_FORM_GNU_strp_alt out of range"); + dwarf_buf_error (buf, "DW_FORM_strp_sup out of range"); return 0; } val->encoding = ATTR_VAL_STRING; @@ -877,6 +1027,95 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, } } +/* If we can determine the value of a string attribute, set *STRING to + point to the string. Return 1 on success, 0 on error. If we don't + know the value, we consider that a success, and we don't change + *STRING. An error is only reported for some sort of out of range + offset. */ + +static int +resolve_string (const struct dwarf_sections *dwarf_sections, int is_dwarf64, + int is_bigendian, uint64_t str_offsets_base, + const struct attr_val *val, + backtrace_error_callback error_callback, void *data, + const char **string) +{ + switch (val->encoding) + { + case ATTR_VAL_STRING: + *string = val->u.string; + return 1; + + case ATTR_VAL_STRING_INDEX: + { + uint64_t offset; + struct dwarf_buf offset_buf; + + offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base; + if (offset + (is_dwarf64 ? 8 : 4) + >= dwarf_sections->size[DEBUG_STR_OFFSETS]) + { + error_callback (data, "DW_FORM_strx value out of range", 0); + return 0; + } + + offset_buf.name = ".debug_str_offsets"; + offset_buf.start = dwarf_sections->data[DEBUG_STR_OFFSETS]; + offset_buf.buf = dwarf_sections->data[DEBUG_STR_OFFSETS] + offset; + offset_buf.left = dwarf_sections->size[DEBUG_STR_OFFSETS] - offset; + offset_buf.is_bigendian = is_bigendian; + offset_buf.error_callback = error_callback; + offset_buf.data = data; + offset_buf.reported_underflow = 0; + + offset = read_offset (&offset_buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_STR]) + { + dwarf_buf_error (&offset_buf, "DW_FORM_strx offset out of range"); + return 0; + } + *string = (const char *) dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + + default: + return 1; + } +} + +/* Set *ADDRESS to the real address for a ATTR_VAL_ADDRESS_INDEX. + Return 1 on success, 0 on error. */ + +static int +resolve_addr_index (const struct dwarf_sections *dwarf_sections, + uint64_t addr_base, int addrsize, int is_bigendian, + uint64_t addr_index, + backtrace_error_callback error_callback, void *data, + uint64_t *address) +{ + uint64_t offset; + struct dwarf_buf addr_buf; + + offset = addr_index * addrsize + addr_base; + if (offset + addrsize >= dwarf_sections->size[DEBUG_ADDR]) + { + error_callback (data, "DW_FORM_addrx value out of range", 0); + return 0; + } + + addr_buf.name = ".debug_addr"; + addr_buf.start = dwarf_sections->data[DEBUG_ADDR]; + addr_buf.buf = dwarf_sections->data[DEBUG_ADDR] + offset; + addr_buf.left = dwarf_sections->size[DEBUG_ADDR] - offset; + addr_buf.is_bigendian = is_bigendian; + addr_buf.error_callback = error_callback; + addr_buf.data = data; + addr_buf.reported_underflow = 0; + + *address = read_address (&addr_buf, addrsize); + return 1; +} + /* Compare a unit offset against a unit for bsearch. */ static int @@ -1142,7 +1381,13 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, read_byte (&count_buf); // Skip attributes. while (read_uleb128 (&count_buf) != 0) - read_uleb128 (&count_buf); + { + uint64_t form; + + form = read_uleb128 (&count_buf); + if ((enum dwarf_form) form == DW_FORM_implicit_const) + read_sleb128 (&count_buf); + } // Skip form of last attribute. read_uleb128 (&count_buf); } @@ -1185,8 +1430,12 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, num_attrs = 0; while (read_uleb128 (&count_buf) != 0) { + uint64_t form; + ++num_attrs; - read_uleb128 (&count_buf); + form = read_uleb128 (&count_buf); + if ((enum dwarf_form) form == DW_FORM_implicit_const) + read_sleb128 (&count_buf); } if (num_attrs == 0) @@ -1214,6 +1463,10 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, break; attrs[num_attrs].name = (enum dwarf_attribute) name; attrs[num_attrs].form = (enum dwarf_form) form; + if ((enum dwarf_form) form == DW_FORM_implicit_const) + attrs[num_attrs].val = read_sleb128 (&abbrev_buf); + else + attrs[num_attrs].val = 0; ++num_attrs; } } @@ -1272,11 +1525,14 @@ lookup_abbrev (struct abbrevs *abbrevs, uint64_t code, struct pcrange { uint64_t lowpc; /* The low PC value. */ int have_lowpc; /* Whether a low PC value was found. */ + int lowpc_is_addr_index; /* Whether lowpc is in .debug_addr. */ uint64_t highpc; /* The high PC value. */ int have_highpc; /* Whether a high PC value was found. */ int highpc_is_relative; /* Whether highpc is relative to lowpc. */ + int highpc_is_addr_index; /* Whether highpc is in .debug_addr. */ uint64_t ranges; /* Offset in ranges section. */ int have_ranges; /* Whether ranges is valid. */ + int ranges_is_index; /* Whether ranges is DW_FORM_rnglistx. */ }; /* Update PCRANGE from an attribute value. */ @@ -1293,6 +1549,12 @@ update_pcrange (const struct attr* attr, const struct attr_val* val, pcrange->lowpc = val->u.uint; pcrange->have_lowpc = 1; } + else if (val->encoding == ATTR_VAL_ADDRESS_INDEX) + { + pcrange->lowpc = val->u.uint; + pcrange->have_lowpc = 1; + pcrange->lowpc_is_addr_index = 1; + } break; case DW_AT_high_pc: @@ -1307,6 +1569,12 @@ update_pcrange (const struct attr* attr, const struct attr_val* val, pcrange->have_highpc = 1; pcrange->highpc_is_relative = 1; } + else if (val->encoding == ATTR_VAL_ADDRESS_INDEX) + { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + pcrange->highpc_is_addr_index = 1; + } break; case DW_AT_ranges: @@ -1316,6 +1584,12 @@ update_pcrange (const struct attr* attr, const struct attr_val* val, pcrange->ranges = val->u.uint; pcrange->have_ranges = 1; } + else if (val->encoding == ATTR_VAL_RNGLISTS_INDEX) + { + pcrange->ranges = val->u.uint; + pcrange->have_ranges = 1; + pcrange->ranges_is_index = 1; + } break; default: @@ -1323,52 +1597,74 @@ update_pcrange (const struct attr* attr, const struct attr_val* val, } } -/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is - passed to ADD_RANGE, and is either a struct unit * or a struct - function *. VEC is the vector we are adding ranges to, and is - either a struct unit_addrs_vector * or a struct function_vector *. - Returns 1 on success, 0 on error. */ +/* Call ADD_RANGE for a low/high PC pair. Returns 1 on success, 0 on + error. */ static int -add_ranges (struct backtrace_state *state, - const struct dwarf_sections *dwarf_sections, - uintptr_t base_address, int is_bigendian, - struct unit *u, uint64_t base, const struct pcrange *pcrange, - int (*add_range) (struct backtrace_state *state, void *rdata, - uint64_t lowpc, uint64_t highpc, - backtrace_error_callback error_callback, - void *data, void *vec), - void *rdata, - backtrace_error_callback error_callback, void *data, - void *vec) +add_low_high_range (struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, + void *rdata, uint64_t lowpc, + uint64_t highpc, + backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) +{ + uint64_t lowpc; + uint64_t highpc; + + lowpc = pcrange->lowpc; + if (pcrange->lowpc_is_addr_index) + { + if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, lowpc, error_callback, data, + &lowpc)) + return 0; + } + + highpc = pcrange->highpc; + if (pcrange->highpc_is_addr_index) + { + if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, highpc, error_callback, data, + &highpc)) + return 0; + } + if (pcrange->highpc_is_relative) + highpc += lowpc; + + /* Add in the base address of the module when recording PC values, + so that we can look up the PC directly. */ + lowpc += base_address; + highpc += base_address; + + return add_range (state, rdata, lowpc, highpc, error_callback, data, vec); +} + +/* Call ADD_RANGE for each range read from .debug_ranges, as used in + DWARF versions 2 through 4. */ + +static int +add_ranges_from_ranges ( + struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, void *data, + void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) { struct dwarf_buf ranges_buf; - if (pcrange->have_lowpc && pcrange->have_highpc) - { - uint64_t lowpc; - uint64_t highpc; - - lowpc = pcrange->lowpc; - highpc = pcrange->highpc; - if (pcrange->highpc_is_relative) - highpc += lowpc; - - /* Add in the base address of the module when recording PC - values, so that we can look up the PC directly. */ - lowpc += base_address; - highpc += base_address; - - return add_range (state, rdata, lowpc, highpc, error_callback, data, - vec); - } - - if (!pcrange->have_ranges) - { - /* Did not find any address ranges to add. */ - return 1; - } - if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES]) { error_callback (data, "ranges offset out of range", 0); @@ -1416,6 +1712,220 @@ add_ranges (struct backtrace_state *state, return 1; } +/* Call ADD_RANGE for each range read from .debug_rnglists, as used in + DWARF version 5. */ + +static int +add_ranges_from_rnglists ( + struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, void *data, + void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) +{ + uint64_t offset; + struct dwarf_buf rnglists_buf; + + if (!pcrange->ranges_is_index) + offset = pcrange->ranges; + else + offset = u->rnglists_base + pcrange->ranges * (u->is_dwarf64 ? 8 : 4); + if (offset >= dwarf_sections->size[DEBUG_RNGLISTS]) + { + error_callback (data, "rnglists offset out of range", 0); + return 0; + } + + rnglists_buf.name = ".debug_rnglists"; + rnglists_buf.start = dwarf_sections->data[DEBUG_RNGLISTS]; + rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset; + rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset; + rnglists_buf.is_bigendian = is_bigendian; + rnglists_buf.error_callback = error_callback; + rnglists_buf.data = data; + rnglists_buf.reported_underflow = 0; + + if (pcrange->ranges_is_index) + { + offset = read_offset (&rnglists_buf, u->is_dwarf64); + offset += u->rnglists_base; + if (offset >= dwarf_sections->size[DEBUG_RNGLISTS]) + { + error_callback (data, "rnglists index offset out of range", 0); + return 0; + } + rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset; + rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset; + } + + while (1) + { + unsigned char rle; + + rle = read_byte (&rnglists_buf); + if (rle == DW_RLE_end_of_list) + break; + switch (rle) + { + case DW_RLE_base_addressx: + { + uint64_t index; + + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &base)) + return 0; + } + break; + + case DW_RLE_startx_endx: + { + uint64_t index; + uint64_t low; + uint64_t high; + + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &low)) + return 0; + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &high)) + return 0; + if (!add_range (state, rdata, low + base_address, + high + base_address, error_callback, data, + vec)) + return 0; + } + break; + + case DW_RLE_startx_length: + { + uint64_t index; + uint64_t low; + uint64_t length; + + index = read_uleb128 (&rnglists_buf); + if (!resolve_addr_index (dwarf_sections, u->addr_base, + u->addrsize, is_bigendian, index, + error_callback, data, &low)) + return 0; + length = read_uleb128 (&rnglists_buf); + low += base_address; + if (!add_range (state, rdata, low, low + length, + error_callback, data, vec)) + return 0; + } + break; + + case DW_RLE_offset_pair: + { + uint64_t low; + uint64_t high; + + low = read_uleb128 (&rnglists_buf); + high = read_uleb128 (&rnglists_buf); + if (!add_range (state, rdata, low + base + base_address, + high + base + base_address, + error_callback, data, vec)) + return 0; + } + break; + + case DW_RLE_base_address: + base = read_address (&rnglists_buf, u->addrsize); + break; + + case DW_RLE_start_end: + { + uint64_t low; + uint64_t high; + + low = read_address (&rnglists_buf, u->addrsize); + high = read_address (&rnglists_buf, u->addrsize); + if (!add_range (state, rdata, low + base_address, + high + base_address, error_callback, data, + vec)) + return 0; + } + break; + + case DW_RLE_start_length: + { + uint64_t low; + uint64_t length; + + low = read_address (&rnglists_buf, u->addrsize); + length = read_uleb128 (&rnglists_buf); + low += base_address; + if (!add_range (state, rdata, low, low + length, + error_callback, data, vec)) + return 0; + } + break; + + default: + dwarf_buf_error (&rnglists_buf, "unrecognized DW_RLE value"); + return 0; + } + } + + if (rnglists_buf.reported_underflow) + return 0; + + return 1; +} + +/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is + passed to ADD_RANGE, and is either a struct unit * or a struct + function *. VEC is the vector we are adding ranges to, and is + either a struct unit_addrs_vector * or a struct function_vector *. + Returns 1 on success, 0 on error. */ + +static int +add_ranges (struct backtrace_state *state, + const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, + struct unit *u, uint64_t base, const struct pcrange *pcrange, + int (*add_range) (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, + backtrace_error_callback error_callback, void *data, + void *vec) +{ + if (pcrange->have_lowpc && pcrange->have_highpc) + return add_low_high_range (state, dwarf_sections, base_address, + is_bigendian, u, pcrange, add_range, rdata, + error_callback, data, vec); + + if (!pcrange->have_ranges) + { + /* Did not find any address ranges to add. */ + return 1; + } + + if (u->version < 5) + return add_ranges_from_ranges (state, dwarf_sections, base_address, + is_bigendian, u, base, pcrange, add_range, + rdata, error_callback, data, vec); + else + return add_ranges_from_rnglists (state, dwarf_sections, base_address, + is_bigendian, u, base, pcrange, add_range, + rdata, error_callback, data, vec); +} + /* Find the address range covered by a compilation unit, reading from UNIT_BUF and adding values to U. Returns 1 if all data could be read, 0 if there is some error. */ @@ -1434,6 +1944,10 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, uint64_t code; const struct abbrev *abbrev; struct pcrange pcrange; + struct attr_val name_val; + int have_name_val; + struct attr_val comp_dir_val; + int have_comp_dir_val; size_t i; code = read_uleb128 (unit_buf); @@ -1448,15 +1962,17 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, *unit_tag = abbrev->tag; memset (&pcrange, 0, sizeof pcrange); + memset (&name_val, 0, sizeof name_val); + have_name_val = 0; + memset (&comp_dir_val, 0, sizeof comp_dir_val); + have_comp_dir_val = 0; for (i = 0; i < abbrev->num_attrs; ++i) { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, unit_buf, - u->is_dwarf64, u->version, u->addrsize, - dwarf_sections->data[DEBUG_STR], - dwarf_sections->size[DEBUG_STR], - altlink, &val)) + if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val, + unit_buf, u->is_dwarf64, u->version, + u->addrsize, dwarf_sections, altlink, &val)) return 0; switch (abbrev->attrs[i].name) @@ -1473,15 +1989,37 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, break; case DW_AT_name: - if (abbrev->tag == DW_TAG_compile_unit - && val.encoding == ATTR_VAL_STRING) - u->filename = val.u.string; + if (abbrev->tag == DW_TAG_compile_unit) + { + name_val = val; + have_name_val = 1; + } break; case DW_AT_comp_dir: + if (abbrev->tag == DW_TAG_compile_unit) + { + comp_dir_val = val; + have_comp_dir_val = 1; + } + break; + + case DW_AT_str_offsets_base: if (abbrev->tag == DW_TAG_compile_unit - && val.encoding == ATTR_VAL_STRING) - u->comp_dir = val.u.string; + && val.encoding == ATTR_VAL_REF_SECTION) + u->str_offsets_base = val.u.uint; + break; + + case DW_AT_addr_base: + if (abbrev->tag == DW_TAG_compile_unit + && val.encoding == ATTR_VAL_REF_SECTION) + u->addr_base = val.u.uint; + break; + + case DW_AT_rnglists_base: + if (abbrev->tag == DW_TAG_compile_unit + && val.encoding == ATTR_VAL_REF_SECTION) + u->rnglists_base = val.u.uint; break; default: @@ -1489,6 +2027,23 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, } } + // Resolve strings after we're sure that we have seen + // DW_AT_str_offsets_base. + if (have_name_val) + { + if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian, + u->str_offsets_base, &name_val, + error_callback, data, &u->filename)) + return 0; + } + if (have_comp_dir_val) + { + if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian, + u->str_offsets_base, &comp_dir_val, + error_callback, data, &u->comp_dir)) + return 0; + } + if (abbrev->tag == DW_TAG_compile_unit || abbrev->tag == DW_TAG_subprogram) { @@ -1565,6 +2120,7 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, int is_dwarf64; struct dwarf_buf unit_buf; int version; + int unit_type; uint64_t abbrev_offset; int addrsize; struct unit *u; @@ -1583,12 +2139,24 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, goto fail; version = read_uint16 (&unit_buf); - if (version < 2 || version > 4) + if (version < 2 || version > 5) { dwarf_buf_error (&unit_buf, "unrecognized DWARF version"); goto fail; } + if (version < 5) + unit_type = 0; + else + { + unit_type = read_byte (&unit_buf); + if (unit_type == DW_UT_type || unit_type == DW_UT_split_type) + { + /* This unit doesn't have anything we need. */ + continue; + } + } + pu = ((struct unit **) backtrace_vector_grow (state, sizeof (struct unit *), error_callback, data, &units)); @@ -1603,6 +2171,11 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, *pu = u; ++units_count; + if (version < 5) + addrsize = 0; /* Set below. */ + else + addrsize = read_byte (&unit_buf); + memset (&u->abbrevs, 0, sizeof u->abbrevs); abbrev_offset = read_offset (&unit_buf, is_dwarf64); if (!read_abbrevs (state, abbrev_offset, @@ -1611,7 +2184,21 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, is_bigendian, error_callback, data, &u->abbrevs)) goto fail; - addrsize = read_byte (&unit_buf); + if (version < 5) + addrsize = read_byte (&unit_buf); + + switch (unit_type) + { + case 0: + break; + case DW_UT_compile: case DW_UT_partial: + break; + case DW_UT_skeleton: case DW_UT_split_compile: + read_uint64 (&unit_buf); /* dwo_id */ + break; + default: + break; + } u->low_offset = unit_offset; unit_offset += len + (is_dwarf64 ? 12 : 4); @@ -1720,26 +2307,334 @@ free_line_header (struct backtrace_state *state, struct line_header *hdr, error_callback, data); } -/* Read the line header. Return 1 on success, 0 on failure. */ +/* Read the directories and file names for a line header for version + 2, setting fields in HDR. Return 1 on success, 0 on failure. */ static int -read_line_header (struct backtrace_state *state, struct unit *u, - int is_dwarf64, struct dwarf_buf *line_buf, - struct line_header *hdr) +read_v2_paths (struct backtrace_state *state, struct unit *u, + struct dwarf_buf *hdr_buf, struct line_header *hdr) { - uint64_t hdrlen; - struct dwarf_buf hdr_buf; const unsigned char *p; const unsigned char *pend; size_t i; + /* Count the number of directory entries. */ + hdr->dirs_count = 0; + p = hdr_buf->buf; + pend = p + hdr_buf->left; + while (p < pend && *p != '\0') + { + p += strnlen((const char *) p, pend - p) + 1; + ++hdr->dirs_count; + } + + hdr->dirs = NULL; + if (hdr->dirs_count != 0) + { + hdr->dirs = ((const char **) + backtrace_alloc (state, + hdr->dirs_count * sizeof (const char *), + hdr_buf->error_callback, + hdr_buf->data)); + if (hdr->dirs == NULL) + return 0; + } + + i = 0; + while (*hdr_buf->buf != '\0') + { + if (hdr_buf->reported_underflow) + return 0; + + hdr->dirs[i] = read_string (hdr_buf); + if (hdr->dirs[i] == NULL) + return 0; + ++i; + } + if (!advance (hdr_buf, 1)) + return 0; + + /* Count the number of file entries. */ + hdr->filenames_count = 0; + p = hdr_buf->buf; + pend = p + hdr_buf->left; + while (p < pend && *p != '\0') + { + p += strnlen ((const char *) p, pend - p) + 1; + p += leb128_len (p); + p += leb128_len (p); + p += leb128_len (p); + ++hdr->filenames_count; + } + + hdr->filenames = ((const char **) + backtrace_alloc (state, + hdr->filenames_count * sizeof (char *), + hdr_buf->error_callback, + hdr_buf->data)); + if (hdr->filenames == NULL) + return 0; + i = 0; + while (*hdr_buf->buf != '\0') + { + const char *filename; + uint64_t dir_index; + + if (hdr_buf->reported_underflow) + return 0; + + filename = read_string (hdr_buf); + if (filename == NULL) + return 0; + dir_index = read_uleb128 (hdr_buf); + if (IS_ABSOLUTE_PATH (filename) + || (dir_index == 0 && u->comp_dir == NULL)) + hdr->filenames[i] = filename; + else + { + const char *dir; + size_t dir_len; + size_t filename_len; + char *s; + + if (dir_index == 0) + dir = u->comp_dir; + else if (dir_index - 1 < hdr->dirs_count) + dir = hdr->dirs[dir_index - 1]; + else + { + dwarf_buf_error (hdr_buf, + ("invalid directory index in " + "line number program header")); + return 0; + } + dir_len = strlen (dir); + filename_len = strlen (filename); + s = ((char *) backtrace_alloc (state, dir_len + filename_len + 2, + hdr_buf->error_callback, + hdr_buf->data)); + if (s == NULL) + return 0; + memcpy (s, dir, dir_len); + /* FIXME: If we are on a DOS-based file system, and the + directory or the file name use backslashes, then we + should use a backslash here. */ + s[dir_len] = '/'; + memcpy (s + dir_len + 1, filename, filename_len + 1); + hdr->filenames[i] = s; + } + + /* Ignore the modification time and size. */ + read_uleb128 (hdr_buf); + read_uleb128 (hdr_buf); + + ++i; + } + + return 1; +} + +/* Read a single version 5 LNCT entry for a directory or file name in a + line header. Sets *STRING to the resulting name, ignoring other + data. Return 1 on success, 0 on failure. */ + +static int +read_lnct (struct backtrace_state *state, struct dwarf_data *ddata, + struct unit *u, struct dwarf_buf *hdr_buf, + const struct line_header *hdr, size_t formats_count, + const struct line_header_format *formats, const char **string) +{ + size_t i; + const char *dir; + const char *path; + + dir = NULL; + path = NULL; + for (i = 0; i < formats_count; i++) + { + struct attr_val val; + + if (!read_attribute (formats[i].form, 0, hdr_buf, u->is_dwarf64, + u->version, hdr->addrsize, &ddata->dwarf_sections, + ddata->altlink, &val)) + return 0; + switch (formats[i].lnct) + { + case DW_LNCT_path: + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, + &val, hdr_buf->error_callback, hdr_buf->data, + &path)) + return 0; + break; + case DW_LNCT_directory_index: + if (val.encoding == ATTR_VAL_UINT) + { + if (val.u.uint >= hdr->dirs_count) + { + dwarf_buf_error (hdr_buf, + ("invalid directory index in " + "line number program header")); + return 0; + } + dir = hdr->dirs[val.u.uint]; + } + break; + default: + /* We don't care about timestamps or sizes or hashes. */ + break; + } + } + + if (path == NULL) + { + dwarf_buf_error (hdr_buf, + "missing file name in line number program header"); + return 0; + } + + if (dir == NULL) + *string = path; + else + { + size_t dir_len; + size_t path_len; + char *s; + + dir_len = strlen (dir); + path_len = strlen (path); + s = (char *) backtrace_alloc (state, dir_len + path_len + 2, + hdr_buf->error_callback, hdr_buf->data); + if (s == NULL) + return 0; + memcpy (s, dir, dir_len); + /* FIXME: If we are on a DOS-based file system, and the + directory or the path name use backslashes, then we should + use a backslash here. */ + s[dir_len] = '/'; + memcpy (s + dir_len + 1, path, path_len + 1); + *string = s; + } + + return 1; +} + +/* Read a set of DWARF 5 line header format entries, setting *PCOUNT + and *PPATHS. Return 1 on success, 0 on failure. */ + +static int +read_line_header_format_entries (struct backtrace_state *state, + struct dwarf_data *ddata, + struct unit *u, + struct dwarf_buf *hdr_buf, + struct line_header *hdr, + size_t *pcount, + const char ***ppaths) +{ + size_t formats_count; + struct line_header_format *formats; + size_t paths_count; + const char **paths; + size_t i; + int ret; + + formats_count = read_byte (hdr_buf); + if (formats_count == 0) + formats = NULL; + else + { + formats = ((struct line_header_format *) + backtrace_alloc (state, + (formats_count + * sizeof (struct line_header_format)), + hdr_buf->error_callback, + hdr_buf->data)); + if (formats == NULL) + return 0; + + for (i = 0; i < formats_count; i++) + { + formats[i].lnct = (int) read_uleb128(hdr_buf); + formats[i].form = (enum dwarf_form) read_uleb128 (hdr_buf); + } + } + + paths_count = read_uleb128 (hdr_buf); + if (paths_count == 0) + { + *pcount = 0; + *ppaths = NULL; + ret = 1; + goto exit; + } + + paths = ((const char **) + backtrace_alloc (state, paths_count * sizeof (const char *), + hdr_buf->error_callback, hdr_buf->data)); + if (paths == NULL) + { + ret = 0; + goto exit; + } + for (i = 0; i < paths_count; i++) + { + if (!read_lnct (state, ddata, u, hdr_buf, hdr, formats_count, + formats, &paths[i])) + { + backtrace_free (state, paths, + paths_count * sizeof (const char *), + hdr_buf->error_callback, hdr_buf->data); + ret = 0; + goto exit; + } + } + + *pcount = paths_count; + *ppaths = paths; + + ret = 1; + + exit: + if (formats != NULL) + backtrace_free (state, formats, + formats_count * sizeof (struct line_header_format), + hdr_buf->error_callback, hdr_buf->data); + + return ret; +} + +/* Read the line header. Return 1 on success, 0 on failure. */ + +static int +read_line_header (struct backtrace_state *state, struct dwarf_data *ddata, + struct unit *u, int is_dwarf64, struct dwarf_buf *line_buf, + struct line_header *hdr) +{ + uint64_t hdrlen; + struct dwarf_buf hdr_buf; + hdr->version = read_uint16 (line_buf); - if (hdr->version < 2 || hdr->version > 4) + if (hdr->version < 2 || hdr->version > 5) { dwarf_buf_error (line_buf, "unsupported line number version"); return 0; } + if (hdr->version < 5) + hdr->addrsize = u->addrsize; + else + { + hdr->addrsize = read_byte (line_buf); + /* We could support a non-zero segment_selector_size but I doubt + we'll ever see it. */ + if (read_byte (line_buf) != 0) + { + dwarf_buf_error (line_buf, + "non-zero segment_selector_size not supported"); + return 0; + } + } + hdrlen = read_offset (line_buf, is_dwarf64); hdr_buf = *line_buf; @@ -1765,116 +2660,21 @@ read_line_header (struct backtrace_state *state, struct unit *u, if (!advance (&hdr_buf, hdr->opcode_base - 1)) return 0; - /* Count the number of directory entries. */ - hdr->dirs_count = 0; - p = hdr_buf.buf; - pend = p + hdr_buf.left; - while (p < pend && *p != '\0') + if (hdr->version < 5) { - p += strnlen((const char *) p, pend - p) + 1; - ++hdr->dirs_count; - } - - hdr->dirs = NULL; - if (hdr->dirs_count != 0) - { - hdr->dirs = ((const char **) - backtrace_alloc (state, - hdr->dirs_count * sizeof (const char *), - line_buf->error_callback, line_buf->data)); - if (hdr->dirs == NULL) + if (!read_v2_paths (state, u, &hdr_buf, hdr)) return 0; } - - i = 0; - while (*hdr_buf.buf != '\0') + else { - if (hdr_buf.reported_underflow) + if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr, + &hdr->dirs_count, + &hdr->dirs)) return 0; - - hdr->dirs[i] = read_string (&hdr_buf); - if (hdr->dirs[i] == NULL) + if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr, + &hdr->filenames_count, + &hdr->filenames)) return 0; - ++i; - } - if (!advance (&hdr_buf, 1)) - return 0; - - /* Count the number of file entries. */ - hdr->filenames_count = 0; - p = hdr_buf.buf; - pend = p + hdr_buf.left; - while (p < pend && *p != '\0') - { - p += strnlen ((const char *) p, pend - p) + 1; - p += leb128_len (p); - p += leb128_len (p); - p += leb128_len (p); - ++hdr->filenames_count; - } - - hdr->filenames = ((const char **) - backtrace_alloc (state, - hdr->filenames_count * sizeof (char *), - line_buf->error_callback, - line_buf->data)); - if (hdr->filenames == NULL) - return 0; - i = 0; - while (*hdr_buf.buf != '\0') - { - const char *filename; - uint64_t dir_index; - - if (hdr_buf.reported_underflow) - return 0; - - filename = read_string (&hdr_buf); - if (filename == NULL) - return 0; - dir_index = read_uleb128 (&hdr_buf); - if (IS_ABSOLUTE_PATH (filename) - || (dir_index == 0 && u->comp_dir == NULL)) - hdr->filenames[i] = filename; - else - { - const char *dir; - size_t dir_len; - size_t filename_len; - char *s; - - if (dir_index == 0) - dir = u->comp_dir; - else if (dir_index - 1 < hdr->dirs_count) - dir = hdr->dirs[dir_index - 1]; - else - { - dwarf_buf_error (line_buf, - ("invalid directory index in " - "line number program header")); - return 0; - } - dir_len = strlen (dir); - filename_len = strlen (filename); - s = ((char *) - backtrace_alloc (state, dir_len + filename_len + 2, - line_buf->error_callback, line_buf->data)); - if (s == NULL) - return 0; - memcpy (s, dir, dir_len); - /* FIXME: If we are on a DOS-based file system, and the - directory or the file name use backslashes, then we - should use a backslash here. */ - s[dir_len] = '/'; - memcpy (s + dir_len + 1, filename, filename_len + 1); - hdr->filenames[i] = s; - } - - /* Ignore the modification time and size. */ - read_uleb128 (&hdr_buf); - read_uleb128 (&hdr_buf); - - ++i; } if (hdr_buf.reported_underflow) @@ -1942,7 +2742,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, lineno = 1; break; case DW_LNE_set_address: - address = read_address (line_buf, u->addrsize); + address = read_address (line_buf, hdr->addrsize); break; case DW_LNE_define_file: { @@ -1965,7 +2765,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, size_t f_len; char *p; - if (dir_index == 0) + if (dir_index == 0 && hdr->version < 5) dir = u->comp_dir; else if (dir_index - 1 < hdr->dirs_count) dir = hdr->dirs[dir_index - 1]; @@ -2129,7 +2929,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata, len = read_initial_length (&line_buf, &is_dwarf64); line_buf.left = len; - if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr)) + if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr)) goto fail; if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec)) @@ -2287,11 +3087,9 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, &unit_buf, - u->is_dwarf64, u->version, u->addrsize, - ddata->dwarf_sections.data[DEBUG_STR], - ddata->dwarf_sections.size[DEBUG_STR], - ddata->altlink, &val)) + if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val, + &unit_buf, u->is_dwarf64, u->version, u->addrsize, + &ddata->dwarf_sections, ddata->altlink, &val)) return NULL; switch (abbrev->attrs[i].name) @@ -2302,15 +3100,26 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, normally not mangled. */ if (ret != NULL) break; - if (val.encoding == ATTR_VAL_STRING) - ret = val.u.string; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, + &val, error_callback, data, &ret)) + return NULL; break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: /* First name preference: override all. */ - if (val.encoding == ATTR_VAL_STRING) - return val.u.string; + { + const char *s; + + s = NULL; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, + &val, error_callback, data, &s)) + return NULL; + if (s != NULL) + return s; + } break; case DW_AT_specification: @@ -2430,19 +3239,28 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, unit_buf, - u->is_dwarf64, u->version, u->addrsize, - ddata->dwarf_sections.data[DEBUG_STR], - ddata->dwarf_sections.size[DEBUG_STR], + if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val, + unit_buf, u->is_dwarf64, u->version, + u->addrsize, &ddata->dwarf_sections, ddata->altlink, &val)) return 0; /* The compile unit sets the base address for any address ranges in the function entries. */ if (abbrev->tag == DW_TAG_compile_unit - && abbrev->attrs[i].name == DW_AT_low_pc - && val.encoding == ATTR_VAL_ADDRESS) - base = val.u.uint; + && abbrev->attrs[i].name == DW_AT_low_pc) + { + if (val.encoding == ATTR_VAL_ADDRESS) + base = val.u.uint; + else if (val.encoding == ATTR_VAL_ADDRESS_INDEX) + { + if (!resolve_addr_index (&ddata->dwarf_sections, + u->addr_base, u->addrsize, + ddata->is_bigendian, val.u.uint, + error_callback, data, &base)) + return 0; + } + } if (is_function) { @@ -2495,18 +3313,31 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, /* Third name preference: don't override. */ if (function->name != NULL) break; - if (val.encoding == ATTR_VAL_STRING) - function->name = val.u.string; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, + u->str_offsets_base, &val, + error_callback, data, &function->name)) + return 0; break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: /* First name preference: override all. */ - if (val.encoding == ATTR_VAL_STRING) - { - function->name = val.u.string; - have_linkage_name = 1; - } + { + const char *s; + + s = NULL; + if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, + u->str_offsets_base, &val, + error_callback, data, &s)) + return 0; + if (s != NULL) + { + function->name = s; + have_linkage_name = 1; + } + } break; case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index 89e0ab7169d..d1d257b1399 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -346,6 +346,10 @@ static const char * const dwarf_section_names[DEBUG_MAX] = ".debug_abbrev", ".debug_ranges", ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" }; /* Information we gather for the sections we care about. */ diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h index 31004bbd954..a9c6688251a 100644 --- a/libbacktrace/internal.h +++ b/libbacktrace/internal.h @@ -295,6 +295,10 @@ enum dwarf_section DEBUG_ABBREV, DEBUG_RANGES, DEBUG_STR, + DEBUG_ADDR, + DEBUG_STR_OFFSETS, + DEBUG_LINE_STR, + DEBUG_RNGLISTS, DEBUG_MAX }; diff --git a/libbacktrace/pecoff.c b/libbacktrace/pecoff.c index fe64a7e695b..f932f35d4cf 100644 --- a/libbacktrace/pecoff.c +++ b/libbacktrace/pecoff.c @@ -141,7 +141,11 @@ static const char * const debug_section_names[DEBUG_MAX] = ".debug_line", ".debug_abbrev", ".debug_ranges", - ".debug_str" + ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" }; /* Information we gather for the sections we care about. */ diff --git a/libbacktrace/xcoff.c b/libbacktrace/xcoff.c index 468b24c418d..a0030f07184 100644 --- a/libbacktrace/xcoff.c +++ b/libbacktrace/xcoff.c @@ -1286,6 +1286,8 @@ xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, + (dwsect[i].offset - min_offset)); } + memset (&dwarf_sections, 0, sizeof dwarf_sections); + dwarf_sections.data[DEBUG_INFO] = dwsect[DEBUG_INFO].data; dwarf_sections.size[DEBUG_INFO] = dwsect[DEBUG_INFO].size; #if BACKTRACE_XCOFF_SIZE == 32