[PATCH] libdw: Recognize zero terminator to end frame table in dwarf_next_cfi.

Mark Wielaard mark@klomp.org
Thu Jun 28 22:26:00 GMT 2018


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 <mark@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  <mark@klomp.org>
+
+	* dwarf_next_cfi.c (dwarf_next_cfi): Check whether length is zero.
+
 2018-06-27  Mark Wielaard  <mark@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  <mark@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  <mark@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



More information about the Elfutils-devel mailing list