When the length is zero this is a the zero terminator that ends the frame table. Return 1 (end of table) instead of -1 (error) in that case. We cannot update next_off and don't want to caller to try again.
Add testcase for dwarf_next_cfi to show both .eh_frame and .debug_frame tables and check consistency (FDEs should point to existing CIEs). Also add a self check to make sure we can read the table from the just build elfutils binaries. Signed-off-by: Mark Wielaard <m...@klomp.org> --- libdw/ChangeLog | 4 ++ libdw/dwarf_next_cfi.c | 8 +++ tests/ChangeLog | 10 ++++ tests/Makefile.am | 8 +-- tests/next_cfi.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ tests/run-next-cfi-self.sh | 21 ++++++++ tests/run-next-cfi.sh | 108 +++++++++++++++++++++++++++++++++++++ 7 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 tests/next_cfi.c create mode 100755 tests/run-next-cfi-self.sh create mode 100755 tests/run-next-cfi.sh diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 97f4f19..11b3c8a 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,7 @@ +2018-06-28 Mark Wielaard <m...@klomp.org> + + * dwarf_next_cfi.c (dwarf_next_cfi): Check whether length is zero. + 2018-06-27 Mark Wielaard <m...@klomp.org> * dwarf_begin_elf.c (check_section): Allow a single .debug_frame diff --git a/libdw/dwarf_next_cfi.c b/libdw/dwarf_next_cfi.c index 53fc369..fa28d99 100644 --- a/libdw/dwarf_next_cfi.c +++ b/libdw/dwarf_next_cfi.c @@ -54,6 +54,7 @@ dwarf_next_cfi (const unsigned char e_ident[], we don't know yet whether this is a 64-bit object or not. */ || unlikely (off + 4 >= data->d_size)) { + done: *next_off = (Dwarf_Off) -1l; return 1; } @@ -79,6 +80,13 @@ dwarf_next_cfi (const unsigned char e_ident[], } length = read_8ubyte_unaligned_inc (&dw, bytes); } + + /* Not explicitly in the DWARF spec, but mentioned in the LSB exception + frames (.eh_frame) spec. If Length contains the value 0, then this + CIE shall be considered a terminator and processing shall end. */ + if (length == 0) + goto done; + if (unlikely ((uint64_t) (limit - bytes) < length) || unlikely (length < offset_size + 1)) goto invalid; diff --git a/tests/ChangeLog b/tests/ChangeLog index 1e03f6b..4ce79f5 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,13 @@ +2018-06-28 Mark Wielaard <m...@klomp.org> + + * next_cfi.c: New file. + * run-next-cfi.sh: New test. + * run-next-cfi-self.sh: Likewise. + * Makefile.am (check_PROGRAMS): Add next_cfi. + (TESTS): Add run-next-cfi.sh and run-next-cfi-self.sh. + (EXTRA_DIST): Likewise. + (next_cfi_LDADD): New variable. + 2018-06-27 Mark Wielaard <m...@klomp.org> * dwarf_cfi.c: New file. diff --git a/tests/Makefile.am b/tests/Makefile.am index 47e5555..ecc2d68 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -59,7 +59,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ elfgetzdata elfputzdata zstrptr emptyfile vendorelf \ fillfile dwarf_default_lower_bound dwarf-die-addr-die \ get-units-invalid get-units-split attr-integrate-skel \ - all-dwarf-ranges unit-info + all-dwarf-ranges unit-info next_cfi asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -151,7 +151,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-get-units-invalid.sh run-get-units-split.sh \ run-attr-integrate-skel.sh \ run-all-dwarf-ranges.sh run-unit-info.sh \ - run-reloc-bpf.sh + run-reloc-bpf.sh \ + run-next-cfi.sh run-next-cfi-self.sh if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -396,7 +397,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ run-attr-integrate-skel.sh \ run-all-dwarf-ranges.sh testfilesplitranges4.debug.bz2 \ testfile-ranges-hello.dwo.bz2 testfile-ranges-world.dwo.bz2 \ - run-unit-info.sh + run-unit-info.sh run-next-cfi.sh run-next-cfi-self.sh if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' @@ -563,6 +564,7 @@ get_units_split_LDADD = $(libdw) attr_integrate_skel_LDADD = $(libdw) all_dwarf_ranges_LDADD = $(libdw) unit_info_LDADD = $(libdw) +next_cfi_LDADD = $(libelf) $(libdw) # We want to test the libelf header against the system elf.h header. # Don't include any -I CPPFLAGS. diff --git a/tests/next_cfi.c b/tests/next_cfi.c new file mode 100644 index 0000000..b923744 --- /dev/null +++ b/tests/next_cfi.c @@ -0,0 +1,131 @@ +/* Test program for dwarf_next_cfi + Copyright (C) 2018 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 <config.h> +#include <assert.h> +#include <inttypes.h> +#include <error.h> +#include ELFUTILS_HEADER(dw) +#include <dwarf.h> +#include <argp.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void +handle_section (const unsigned char e_ident[], + Elf_Scn *scn, const bool is_eh) +{ + if (is_eh) + printf (".eh_frame\n"); + else + printf (".debug_frame\n"); + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL || data->d_buf == NULL) + error (EXIT_FAILURE, 0, "no section data"); + + int res; + Dwarf_Off off; + Dwarf_Off next_off = 0; + Dwarf_CFI_Entry entry; + while ((res = dwarf_next_cfi (e_ident, data, is_eh, off = next_off, + &next_off, &entry)) == 0) + { + printf ("[%" PRId64 "] ", off); + if (dwarf_cfi_cie_p (&entry)) + printf ("CIE augmentation=\"%s\"\n", entry.cie.augmentation); + else + { + printf ("FDE cie=[%" PRId64 "]\n", entry.fde.CIE_pointer); + + Dwarf_Off cie_off = entry.fde.CIE_pointer; + Dwarf_Off cie_off_next; + Dwarf_CFI_Entry cie_entry; + if (dwarf_next_cfi (e_ident, data, is_eh, cie_off, &cie_off_next, + &cie_entry) != 0 + || !dwarf_cfi_cie_p (&cie_entry)) + error (EXIT_FAILURE, 0, "FDE doesn't point to CIE"); + } + } + + if (res < 0) + error (EXIT_FAILURE, 0, "dwarf_next_cfi failed: %s\n", + dwarf_errmsg (-1)); +} + +int +main (int argc, char *argv[]) +{ + if (argc != 2) + error (EXIT_FAILURE, 0, "need file name argument"); + + const char *file = argv[1]; + printf ("%s\n", file); + + int fd = open (file, O_RDONLY); + if (fd == -1) + error (EXIT_FAILURE, errno, "cannot open input file `%s'", file); + + elf_version (EV_CURRENT); + + Elf *elf = elf_begin (fd, ELF_C_READ, NULL); + if (elf == NULL) + error (EXIT_FAILURE, 0, "cannot create ELF descriptor: %s", + elf_errmsg (-1)); + + size_t esize; + const unsigned char *ident = (const unsigned char *) elf_getident (elf, + &esize); + if (ident == NULL || esize < EI_NIDENT) + error (EXIT_FAILURE, 0, "no, or too small, ELF ident"); + + GElf_Ehdr ehdr; + if (gelf_getehdr (elf, &ehdr) == NULL) + error (EXIT_FAILURE, 0, "cannot get the ELF header: %s\n", + elf_errmsg (-1)); + + size_t strndx = ehdr.e_shstrndx; + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr; + if (gelf_getshdr (scn, &shdr) != NULL) + { + char *name = elf_strptr (elf, strndx, (size_t) shdr.sh_name); + if (name != NULL && shdr.sh_type == SHT_PROGBITS) + { + if (strcmp (name, ".eh_frame") == 0) + handle_section (ident, scn, true); + if (strcmp (name, ".debug_frame") == 0) + handle_section (ident, scn, false); + } + } + } + + elf_end (elf); + close (fd); + + return 0; +} diff --git a/tests/run-next-cfi-self.sh b/tests/run-next-cfi-self.sh new file mode 100755 index 0000000..2c42ea3 --- /dev/null +++ b/tests/run-next-cfi-self.sh @@ -0,0 +1,21 @@ +#! /bin/sh +# Copyright (C) 2012 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 + +# Sanity check toolchain/dwarf_next_cfi +testrun_on_self ${abs_builddir}/next_cfi diff --git a/tests/run-next-cfi.sh b/tests/run-next-cfi.sh new file mode 100755 index 0000000..23c0d7d --- /dev/null +++ b/tests/run-next-cfi.sh @@ -0,0 +1,108 @@ +#! /bin/sh +# Test for dwarf_next_cfi. +# Copyright (C) 2018 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 + +# Test files come from run-addrcfi +testfiles testfile11 testfile12 +testfiles testfilearm testfileaarch64 +testfiles testfileppc32 testfileppc64 + +testrun_compare ${abs_builddir}/next_cfi testfile11 <<\EOF +testfile11 +.eh_frame +[0] CIE augmentation="zPL" +[28] FDE cie=[0] +[64] FDE cie=[0] +[96] FDE cie=[0] +[128] FDE cie=[0] +.debug_frame +[0] CIE augmentation="" +[20] FDE cie=[0] +[52] FDE cie=[0] +[76] FDE cie=[0] +[100] FDE cie=[0] +[124] FDE cie=[0] +[148] FDE cie=[0] +[172] FDE cie=[0] +[196] FDE cie=[0] +EOF + +testrun_compare ${abs_builddir}/next_cfi testfile12 <<\EOF +testfile12 +.eh_frame +[0] CIE augmentation="" +[16] CIE augmentation="zR" +[40] FDE cie=[16] +.debug_frame +[0] CIE augmentation="" +[24] FDE cie=[0] +EOF + +testrun_compare ${abs_builddir}/next_cfi testfilearm <<\EOF +testfilearm +.eh_frame +.debug_frame +[0] CIE augmentation="" +[16] FDE cie=[0] +[32] CIE augmentation="" +[48] FDE cie=[32] +EOF + +testrun_compare ${abs_builddir}/next_cfi testfileaarch64 <<\EOF +testfileaarch64 +.eh_frame +[0] CIE augmentation="zR" +[24] FDE cie=[0] +[80] FDE cie=[0] +.debug_frame +[0] CIE augmentation="" +[16] FDE cie=[0] +[40] CIE augmentation="" +[56] FDE cie=[40] +EOF + +testrun_compare ${abs_builddir}/next_cfi testfileppc32 <<\EOF +testfileppc32 +.eh_frame +[0] CIE augmentation="zR" +[20] FDE cie=[0] +[40] FDE cie=[0] +[96] FDE cie=[0] +.debug_frame +[0] CIE augmentation="" +[16] FDE cie=[0] +[32] CIE augmentation="" +[48] FDE cie=[32] +EOF + +testrun_compare ${abs_builddir}/next_cfi testfileppc64 <<\EOF +testfileppc64 +.eh_frame +[0] CIE augmentation="zR" +[20] FDE cie=[0] +[40] FDE cie=[0] +[64] CIE augmentation="zR" +[88] FDE cie=[64] +[144] FDE cie=[64] +.debug_frame +[0] CIE augmentation="" +[16] FDE cie=[0] +[56] CIE augmentation="" +[72] FDE cie=[56] +EOF -- 1.8.3.1