[PATCH] libdw,readelf: Recognize DW_CFA_AARCH64_negate_ra_state

Mark Wielaard mark@klomp.org
Thu Sep 3 15:58:23 GMT 2020


DW_CFA_AARCH64_negate_ra_state is used on aarch64 to indicate whether
or not the return address is mangled or not. This has the same value
as the DW_CFA_GNU_window_save. So we have to pass around the e_machine
value of the process or core we are inspecting to know which one to
use.

Note that it isn't actually implemented yet. It needs ARMv8.3 hardware.
If we don't have such hardware it is enough to simply ignore the
DW_CFA_AARCH64_negate_ra_state (and not confuse it with
DW_CFA_GNU_window_save) to get backtraces to work on aarch64.

Add a testcase for eu-readelf --debug-dump=frames to show the value
is correctly recognized. Also don't warn we cannot find any DWARF
if we are just dumping frames (those will come from .eh_frame if
there is no .debug_frame).

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libdw/ChangeLog             |  12 +++
 libdw/cfi.c                 |  41 +++++----
 libdw/cfi.h                 |   2 +
 libdw/dwarf.h               |   1 +
 libdw/dwarf_getcfi.c        |   5 ++
 libdw/dwarf_getcfi_elf.c    |   8 +-
 src/ChangeLog               |   7 ++
 src/readelf.c               |  13 +--
 tests/ChangeLog             |   6 ++
 tests/Makefile.am           |   2 +
 tests/run-readelf-frames.sh | 173 ++++++++++++++++++++++++++++++++++++
 11 files changed, 247 insertions(+), 23 deletions(-)
 create mode 100755 tests/run-readelf-frames.sh

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 796dbfb7..fc2d872c 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,15 @@
+2020-09-03  Mark Wielaard  <mark@klomp.org>
+
+	* dwarf.h: Add DW_CFA_AARCH64_negate_ra_state.
+	* cfi.h (struct Dwarf_CFI_s): Add e_machine field.
+	* cfi.c (execute_cfi): Recognize, but ignore
+	DW_CFA_AARCH64_negate_ra_state on aarch64.
+	* dwarf_getcfi.c (dwarf_getcfi): Set cfi e_machine.
+	* dwarf_getcfi_elf.c (allocate_cfi): Take ehdr as argument and set
+	cfi e_machine.
+	(getcfi_gnu_eh_frame): Pass ehdr to allocate_cfi.
+	(getcfi_scn_eh_frame): Likewise.
+
 2020-08-25  Mark Wielaard  <mark@klomp.org>
 
 	* dwarf_getlocation.c (check_constant_offset): Rename to...
diff --git a/libdw/cfi.c b/libdw/cfi.c
index 6705294f..a73fb03f 100644
--- a/libdw/cfi.c
+++ b/libdw/cfi.c
@@ -350,24 +350,35 @@ execute_cfi (Dwarf_CFI *cache,
 	case DW_CFA_nop:
 	  continue;
 
-	case DW_CFA_GNU_window_save:
-	  /* This is magic shorthand used only by SPARC.  It's equivalent
-	     to a bunch of DW_CFA_register and DW_CFA_offset operations.  */
-	  if (unlikely (! enough_registers (31, &fs, &result)))
-	    goto out;
-	  for (regno = 8; regno < 16; ++regno)
+	case DW_CFA_GNU_window_save: /* DW_CFA_AARCH64_negate_ra_state */
+	  if (cache->e_machine == EM_AARCH64)
 	    {
-	      /* Find each %oN in %iN.  */
-	      fs->regs[regno].rule = reg_register;
-	      fs->regs[regno].value = regno + 16;
+	      /* Toggles the return address state, indicating whether
+		 the return address is encrypted or not on
+		 aarch64. XXX not handled yet.  */
 	    }
-	  unsigned int address_size = (cache->e_ident[EI_CLASS] == ELFCLASS32
-				       ? 4 : 8);
-	  for (; regno < 32; ++regno)
+	  else
 	    {
-	      /* Find %l0..%l7 and %i0..%i7 in a block at the CFA.  */
-	      fs->regs[regno].rule = reg_offset;
-	      fs->regs[regno].value = (regno - 16) * address_size;
+	      /* This is magic shorthand used only by SPARC.  It's
+		 equivalent to a bunch of DW_CFA_register and
+		 DW_CFA_offset operations.  */
+	      if (unlikely (! enough_registers (31, &fs, &result)))
+		goto out;
+	      for (regno = 8; regno < 16; ++regno)
+		{
+		  /* Find each %oN in %iN.  */
+		  fs->regs[regno].rule = reg_register;
+		  fs->regs[regno].value = regno + 16;
+		}
+	      unsigned int address_size;
+	      address_size = (cache->e_ident[EI_CLASS] == ELFCLASS32
+			      ? 4 : 8);
+	      for (; regno < 32; ++regno)
+		{
+		  /* Find %l0..%l7 and %i0..%i7 in a block at the CFA.  */
+		  fs->regs[regno].rule = reg_offset;
+		  fs->regs[regno].value = (regno - 16) * address_size;
+		}
 	    }
 	  continue;
 
diff --git a/libdw/cfi.h b/libdw/cfi.h
index 1ebf2dc3..1b0d712f 100644
--- a/libdw/cfi.h
+++ b/libdw/cfi.h
@@ -108,6 +108,8 @@ struct Dwarf_CFI_s
   size_t search_table_entries;
   uint8_t search_table_encoding;
 
+  uint16_t e_machine;
+
   /* True if the file has a byte order different from the host.  */
   bool other_byte_order;
 
diff --git a/libdw/dwarf.h b/libdw/dwarf.h
index 71ca2baa..98b10437 100644
--- a/libdw/dwarf.h
+++ b/libdw/dwarf.h
@@ -966,6 +966,7 @@ enum
     DW_CFA_low_user = 0x1c,
     DW_CFA_MIPS_advance_loc8 = 0x1d,
     DW_CFA_GNU_window_save = 0x2d,
+    DW_CFA_AARCH64_negate_ra_state = 0x2d,
     DW_CFA_GNU_args_size = 0x2e,
     DW_CFA_GNU_negative_offset_extended = 0x2f,
     DW_CFA_high_user = 0x3f
diff --git a/libdw/dwarf_getcfi.c b/libdw/dwarf_getcfi.c
index 51932cd9..afa8a460 100644
--- a/libdw/dwarf_getcfi.c
+++ b/libdw/dwarf_getcfi.c
@@ -57,6 +57,11 @@ dwarf_getcfi (Dwarf *dbg)
       cfi->datarel = 0;
 
       cfi->e_ident = (unsigned char *) elf_getident (dbg->elf, NULL);
+
+      GElf_Ehdr ehdr;
+      gelf_getehdr (dbg->elf, &ehdr);
+      cfi->e_machine = ehdr.e_machine;
+
       cfi->other_byte_order = dbg->other_byte_order;
       cfi->default_same_value = false;
 
diff --git a/libdw/dwarf_getcfi_elf.c b/libdw/dwarf_getcfi_elf.c
index adcaea03..c0e3cadd 100644
--- a/libdw/dwarf_getcfi_elf.c
+++ b/libdw/dwarf_getcfi_elf.c
@@ -41,7 +41,7 @@
 
 
 static Dwarf_CFI *
-allocate_cfi (Elf *elf, GElf_Addr vaddr)
+allocate_cfi (Elf *elf, const GElf_Ehdr *ehdr, GElf_Addr vaddr)
 {
   Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
   if (cfi == NULL)
@@ -58,6 +58,8 @@ allocate_cfi (Elf *elf, GElf_Addr vaddr)
       return NULL;
     }
 
+  cfi->e_machine = ehdr->e_machine;
+
   if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
       || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
     cfi->other_byte_order = true;
@@ -172,7 +174,7 @@ getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
       __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
       return NULL;
     }
-  Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
+  Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, eh_frame_ptr);
   if (cfi != NULL)
     {
       cfi->data = (Elf_Data_Scn *) data;
@@ -222,7 +224,7 @@ getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
       __libdw_seterrno (DWARF_E_INVALID_ELF);
       return NULL;
     }
-  Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
+  Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, shdr->sh_addr);
   if (cfi != NULL)
     {
       cfi->data = (Elf_Data_Scn *) data;
diff --git a/src/ChangeLog b/src/ChangeLog
index 8eb2103d..82b6919f 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2020-09-03  Mark Wielaard  <mark@klomp.org>
+
+	* readelf.c (print_cfa_program): Take ehdr as argument. Use it to
+	recognize DW_CFA_AARCH64_negate_ra_state.
+	(print_debug_frame_section): Pass ehdr to print_cfa_program.
+	(print_debug): Don't warn if we dump frames, but cannot get dbg.
+
 2020-08-26  Mark Wielaard  <mark@klomp.org>
 
 	* readelf.c (print_debug_line_section): It is not an error if there
diff --git a/src/readelf.c b/src/readelf.c
index 41933d95..feb66b09 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -6176,7 +6176,7 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
 		   int data_align,
 		   unsigned int version, unsigned int ptr_size,
 		   unsigned int encoding,
-		   Dwfl_Module *dwflmod, Ebl *ebl, Dwarf *dbg)
+		   Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, Dwarf *dbg)
 {
   char regnamebuf[REGNAMESZ];
   const char *regname (unsigned int regno)
@@ -6399,8 +6399,11 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
 	    printf ("     MIPS_advance_loc8 %" PRIu64 " to %#" PRIx64 "\n",
 		    op1, pc += op1 * code_align);
 	    break;
-	  case DW_CFA_GNU_window_save:
-	    puts ("     GNU_window_save");
+	  case DW_CFA_GNU_window_save:  /* DW_CFA_AARCH64_negate_ra_state  */
+	    if (ehdr->e_machine == EM_AARCH64)
+	      puts ("     AARCH64_negate_ra_state");
+	    else
+	      puts ("     GNU_window_save");
 	    break;
 	  case DW_CFA_GNU_args_size:
 	    if ((uint64_t) (endp - readp) < 1)
@@ -6930,7 +6933,7 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
       else
 	print_cfa_program (readp, cieend, vma_base, code_alignment_factor,
 			   data_alignment_factor, version, ptr_size,
-			   fde_encoding, dwflmod, ebl, dbg);
+			   fde_encoding, dwflmod, ebl, ehdr, dbg);
       readp = cieend;
     }
 }
@@ -11088,7 +11091,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
     };
   if (dbg == NULL)
     {
-      if ((print_debug_sections & ~section_exception) != 0)
+      if ((print_debug_sections & ~(section_exception|section_frame)) != 0)
 	error (0, 0, gettext ("cannot get debug context descriptor: %s"),
 	       dwfl_errmsg (-1));
       dbg = &dummy_dbg;
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 61c6cb7c..5f2b1449 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2020-09-03  Mark Wielaard  <mark@klomp.org>
+
+	* run-readelf-frames.sh: New test.
+	* Makefile.am (TESTS): Add run-readelf-frames.sh.
+	(EXTRA_DIST): Likewise.
+
 2020-09-03  Mark Wielaard  <mark@klomp.org>
 
 	* testfile-gnu-property-note-aarch64.bz2: New file.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 939bbb6a..4629ce64 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -125,6 +125,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	run-readelf-test4.sh run-readelf-twofiles.sh \
 	run-readelf-macro.sh run-readelf-loc.sh run-readelf-ranges.sh \
 	run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \
+	run-readelf-frames.sh \
 	run-readelf-n.sh \
 	run-native-test.sh run-bug1-test.sh \
 	run-debuglink.sh run-debugaltlink.sh run-buildid.sh \
@@ -313,6 +314,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \
 	     run-readelf-addr.sh run-readelf-str.sh \
 	     run-readelf-types.sh \
+	     run-readelf-frames.sh \
 	     run-readelf-n.sh \
 	     testfile-gnu-property-note.bz2 testfile-gnu-property-note.o.bz2 \
 	     testfile_gnu_props.32le.o.bz2 \
diff --git a/tests/run-readelf-frames.sh b/tests/run-readelf-frames.sh
new file mode 100755
index 00000000..f0429d19
--- /dev/null
+++ b/tests/run-readelf-frames.sh
@@ -0,0 +1,173 @@
+#! /bin/sh
+# Copyright (C) 2020 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
+
+# See run-readelf-n.sh
+testfiles testfile-gnu-property-note-aarch64
+
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=frames testfile-gnu-property-note-aarch64 <<\EOF
+
+Call frame search table section [17] '.eh_frame_hdr':
+ version:          1
+ eh_frame_ptr_enc: 0x1b (sdata4 pcrel)
+ fde_count_enc:    0x3 (udata4)
+ table_enc:        0x3b (sdata4 datarel)
+ eh_frame_ptr:     0x44 (offset: 0x758)
+ fde_count:        7
+ Table:
+  0xfffffe70 (offset:  0x580) -> 0x5c fde=[    14]
+  0xfffffea0 (offset:  0x5b0) -> 0x70 fde=[    28]
+  0xfffffee0 (offset:  0x5f0) -> 0x84 fde=[    3c]
+  0xffffff20 (offset:  0x630) -> 0xac fde=[    64]
+  0xffffff28 (offset:  0x638) -> 0xc0 fde=[    78]
+  0xffffff40 (offset:  0x650) -> 0xd8 fde=[    90]
+  0xffffffc0 (offset:  0x6d0) -> 0x110 fde=[    c8]
+
+Call frame information section [18] '.eh_frame' at offset 0x758:
+
+ [     0] CIE length=16
+   CIE_id:                   0
+   version:                  1
+   augmentation:             "zR"
+   code_alignment_factor:    4
+   data_alignment_factor:    -8
+   return_address_register:  30
+   Augmentation data:        0x1b (FDE address encoding: sdata4 pcrel)
+
+   Program:
+     def_cfa r31 (sp) at offset 0
+
+ [    14] FDE length=16 cie=[     0]
+   CIE_pointer:              24
+   initial_location:         0x0000000000400580 (offset: 0x580)
+   address_range:            0x30 (end offset: 0x5b0)
+
+   Program:
+     nop
+     nop
+     nop
+
+ [    28] FDE length=16 cie=[     0]
+   CIE_pointer:              44
+   initial_location:         0x00000000004005b0 (offset: 0x5b0)
+   address_range:            0x3c (end offset: 0x5ec)
+
+   Program:
+     nop
+     nop
+     nop
+
+ [    3c] FDE length=36 cie=[     0]
+   CIE_pointer:              64
+   initial_location:         0x00000000004005f0 (offset: 0x5f0)
+   address_range:            0x38 (end offset: 0x628)
+
+   Program:
+     advance_loc 1 to 0x5f4
+     AARCH64_negate_ra_state
+     advance_loc 1 to 0x5f8
+     def_cfa_offset 32
+     offset r29 (x29) at cfa-32
+     offset r30 (x30) at cfa-24
+     advance_loc 2 to 0x600
+     offset r19 (x19) at cfa-16
+     advance_loc 8 to 0x620
+     restore r30 (x30)
+     restore r29 (x29)
+     restore r19 (x19)
+     def_cfa_offset 0
+     advance_loc 1 to 0x624
+     AARCH64_negate_ra_state
+     nop
+     nop
+     nop
+
+ [    64] FDE length=16 cie=[     0]
+   CIE_pointer:              104
+   initial_location:         0x0000000000400630 (offset: 0x630)
+   address_range:            0x8 (end offset: 0x638)
+
+   Program:
+     nop
+     nop
+     nop
+
+ [    78] FDE length=20 cie=[     0]
+   CIE_pointer:              124
+   initial_location:         0x0000000000400638 (offset: 0x638)
+   address_range:            0xc (end offset: 0x644)
+
+   Program:
+     nop
+     nop
+     nop
+     nop
+     nop
+     nop
+     nop
+
+ [    90] FDE length=52 cie=[     0]
+   CIE_pointer:              148
+   initial_location:         0x0000000000400650 (offset: 0x650)
+   address_range:            0x80 (end offset: 0x6d0)
+
+   Program:
+     advance_loc 1 to 0x654
+     AARCH64_negate_ra_state
+     advance_loc 1 to 0x658
+     def_cfa_offset 64
+     offset r29 (x29) at cfa-64
+     offset r30 (x30) at cfa-56
+     advance_loc 2 to 0x660
+     offset r19 (x19) at cfa-48
+     offset r20 (x20) at cfa-40
+     advance_loc 3 to 0x66c
+     offset r21 (x21) at cfa-32
+     offset r22 (x22) at cfa-24
+     advance_loc 5 to 0x680
+     offset r23 (x23) at cfa-16
+     offset r24 (x24) at cfa-8
+     advance_loc 18 to 0x6c8
+     restore r30 (x30)
+     restore r29 (x29)
+     restore r23 (x23)
+     restore r24 (x24)
+     restore r21 (x21)
+     restore r22 (x22)
+     restore r19 (x19)
+     restore r20 (x20)
+     def_cfa_offset 0
+     advance_loc 1 to 0x6cc
+     AARCH64_negate_ra_state
+     nop
+     nop
+
+ [    c8] FDE length=16 cie=[     0]
+   CIE_pointer:              204
+   initial_location:         0x00000000004006d0 (offset: 0x6d0)
+   address_range:            0x8 (end offset: 0x6d8)
+
+   Program:
+     nop
+     nop
+     nop
+
+ [    dc] Zero terminator
+EOF
+
+exit 0
-- 
2.18.4



More information about the Elfutils-devel mailing list