Make sure the testcases (library functions they use) don't crash, triggers self-check/asserts or leaks memory under valgrind. This also helps making sure newer DWARF constructs are handled (when building with -gdwarf-5).
Signed-off-by: Mark Wielaard <m...@klomp.org> --- tests/ChangeLog | 16 ++++++++++++ tests/Makefile.am | 2 ++ tests/run-exprlocs-self.sh | 22 ++++++++++++++++ tests/run-varlocs-self.sh | 22 ++++++++++++++++ tests/varlocs.c | 64 ++++++++++++++++++++++++++++++++++------------ 5 files changed, 110 insertions(+), 16 deletions(-) create mode 100755 tests/run-exprlocs-self.sh create mode 100755 tests/run-varlocs-self.sh diff --git a/tests/ChangeLog b/tests/ChangeLog index f39a027..6c8a8e1 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,19 @@ +2017-11-10 Mark Wielaard <m...@klomp.org> + + * run-exprlocs-self.sh: New test. + * run-varlocs-self.sh: Likewise. + * Makefile.am (TESTS) Add run-exprlocs-self.sh and + run-varlocs-self.sh. + (EXTRA_DIST): Likewise. + * varlocs.c (cfi_debug_bias): New global variable. + (is_ET_REL): Likewise. + (print_expr): Don't crash and burn when CFI cannot be found for an + ET_REL file for DW_OP_call_frame_cfa. + (handle_die): If there is no entry_pc pick the lowest pc start range + for the DIE. + (main): Check at least one CU was found. Use dwfl_module_dwarf_cfi + and dwfl_module_eh_cfi to fix memory leak. Set is_ET_REL. + 2017-11-03 Mark Wielaard <m...@klomp.org> * run-exprlocs.sh: New test. diff --git a/tests/Makefile.am b/tests/Makefile.am index f992b12..d502054 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -92,6 +92,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-addrscopes.sh run-strings-test.sh run-funcscopes.sh \ run-find-prologues.sh run-allregs.sh run-addrcfi.sh \ run-nm-self.sh run-readelf-self.sh \ + run-varlocs-self.sh run-exprlocs-self.sh \ run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \ run-readelf-test4.sh run-readelf-twofiles.sh \ run-readelf-macro.sh run-readelf-loc.sh \ @@ -189,6 +190,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ run-ranlib-test3.sh run-ranlib-test4.sh \ run-addrscopes.sh run-strings-test.sh run-funcscopes.sh \ run-nm-self.sh run-readelf-self.sh run-addrcfi.sh \ + run-varlocs-self.sh run-exprlocs-self.sh \ run-find-prologues.sh run-allregs.sh run-native-test.sh \ run-addrname-test.sh run-dwfl-bug-offline-rel.sh \ run-dwfl-addr-sect.sh run-early-offscn.sh \ diff --git a/tests/run-exprlocs-self.sh b/tests/run-exprlocs-self.sh new file mode 100755 index 0000000..73d3ab9 --- /dev/null +++ b/tests/run-exprlocs-self.sh @@ -0,0 +1,22 @@ +#! /bin/sh +# Copyright (C) 2017 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/test-subr.sh + +# Just makes sure exprlocs doesn't crash, triggers self-check/asserts +# or leaks memory under valgrind. +testrun_on_self_quiet ${abs_top_builddir}/tests/varlocs --exprlocs -e diff --git a/tests/run-varlocs-self.sh b/tests/run-varlocs-self.sh new file mode 100755 index 0000000..54b6a8d --- /dev/null +++ b/tests/run-varlocs-self.sh @@ -0,0 +1,22 @@ +#! /bin/sh +# Copyright (C) 2017 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/test-subr.sh + +# Make sure varlocs doesn't crash, doesn't trigger self-check/asserts +# or leaks running under valgrind. +testrun_on_self_quiet ${abs_top_builddir}/tests/varlocs -e diff --git a/tests/varlocs.c b/tests/varlocs.c index cc77559..3267198 100644 --- a/tests/varlocs.c +++ b/tests/varlocs.c @@ -39,9 +39,12 @@ // Needed for DW_OP_call_frame_cfa. static Dwarf *dw; Dwarf_CFI *cfi_debug; +Dwarf_Addr cfi_debug_bias; Dwarf_CFI *cfi_eh; Dwarf_Addr cfi_eh_bias; +bool is_ET_REL; + // Whether the current function has a DW_AT_frame_base defined. // Needed for DW_OP_fbreg. bool has_frame_base; @@ -258,20 +261,29 @@ print_expr (Dwarf_Attribute *attr, Dwarf_Op *expr, Dwarf_Addr addr) error (EXIT_FAILURE, 0, "DW_OP_call_frame_cfa used but no cfi found."); Dwarf_Frame *frame; - if (dwarf_cfi_addrframe (cfi_eh, addr + cfi_eh_bias, &frame) != 0 - && dwarf_cfi_addrframe (cfi_debug, addr, &frame) != 0) + if (dwarf_cfi_addrframe (cfi_eh, addr + cfi_eh_bias, &frame) == 0 + || dwarf_cfi_addrframe (cfi_debug, addr + cfi_debug_bias, + &frame) == 0) + { + Dwarf_Op *cfa_ops; + size_t cfa_nops; + if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0) + error (EXIT_FAILURE, 0, "dwarf_frame_cfa 0x%" PRIx64 ": %s", + addr, dwarf_errmsg (-1)); + if (cfa_nops < 1) + error (EXIT_FAILURE, 0, "dwarf_frame_cfa no ops"); + print_expr_block (NULL, cfa_ops, cfa_nops, 0); + free (frame); + } + else if (is_ET_REL) + { + /* XXX In ET_REL files there might be an .eh_frame with relocations + we don't handle (e.g. X86_64_PC32). Maybe we should? */ + printf ("{...}\n"); + } + else error (EXIT_FAILURE, 0, "dwarf_cfi_addrframe 0x%" PRIx64 ": %s", addr, dwarf_errmsg (-1)); - - Dwarf_Op *cfa_ops; - size_t cfa_nops; - if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0) - error (EXIT_FAILURE, 0, "dwarf_frame_cfa 0x%" PRIx64 ": %s", - addr, dwarf_errmsg (-1)); - if (cfa_nops < 1) - error (EXIT_FAILURE, 0, "dwarf_frame_cfa no ops"); - print_expr_block (NULL, cfa_ops, cfa_nops, 0); - free (frame); break; case DW_OP_push_object_address: @@ -924,7 +936,14 @@ handle_die (Dwarf_Die *die, int depth, bool outer_has_frame_base, on address. */ Dwarf_Addr die_entrypc; if (dwarf_entrypc (die, &die_entrypc) != 0 || die_entrypc == 0) - die_entrypc = outer_entrypc; + { + /* Try to get the lowest address of the first range covered. */ + Dwarf_Addr base, start, end; + if (dwarf_ranges (die, 0, &base, &start, &end) <= 0 || start == 0) + die_entrypc = outer_entrypc; + else + die_entrypc = start; + } arg.entrypc = die_entrypc; /* Whether this or the any outer DIE has a frame base. Used as @@ -973,6 +992,7 @@ main (int argc, char *argv[]) Dwarf_Die *cu = NULL; Dwarf_Addr dwbias; + bool found_cu = false; while ((cu = dwfl_nextcu (dwfl, cu, &dwbias)) != NULL) { /* Only walk actual compile units (not partial units) that @@ -982,6 +1002,8 @@ main (int argc, char *argv[]) if (dwarf_tag (cu) == DW_TAG_compile_unit && (exprlocs || dwarf_lowpc (cu, &cubase) == 0)) { + found_cu = true; + Dwfl_Module *mod = dwfl_cumodule (cu); Dwarf_Addr modbias; dw = dwfl_module_getdwarf (mod, &modbias); @@ -1006,9 +1028,16 @@ main (int argc, char *argv[]) Elf *elf = dwfl_module_getelf (mod, &elfbias); // CFI. We need both since sometimes neither is complete. - cfi_debug = dwarf_getcfi (dw); // No bias needed, same file. - cfi_eh = dwarf_getcfi_elf (elf); - cfi_eh_bias = dwbias - elfbias; + cfi_debug = dwfl_module_dwarf_cfi (mod, &cfi_debug_bias); + cfi_eh = dwfl_module_eh_cfi (mod, &cfi_eh_bias); + + assert (cfi_debug == 0); // No bias needed, same file. + + // We are a bit forgiving for object files. There might be + // relocations we don't handle that are needed in some + // places... + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); + is_ET_REL = ehdr->e_type == ET_REL; // Get the actual CU DIE and walk all all DIEs (or just the // functions) inside it. @@ -1037,6 +1066,9 @@ main (int argc, char *argv[]) } } + if (! found_cu) + error (EXIT_FAILURE, 0, "No DWARF CU found?"); + dwfl_end (dwfl); return 0; } -- 1.8.3.1