]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw,readelf: Recognize DW_CFA_AARCH64_negate_ra_state
authorMark Wielaard <mark@klomp.org>
Thu, 3 Sep 2020 15:46:53 +0000 (17:46 +0200)
committerMark Wielaard <mark@klomp.org>
Mon, 7 Sep 2020 10:21:10 +0000 (12:21 +0200)
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
libdw/cfi.c
libdw/cfi.h
libdw/dwarf.h
libdw/dwarf_getcfi.c
libdw/dwarf_getcfi_elf.c
src/ChangeLog
src/readelf.c
tests/ChangeLog
tests/Makefile.am
tests/run-readelf-frames.sh [new file with mode: 0755]

index 13683ef09f709c1537d535609615b122602355cd..8b0b583a1ba60510b3b3885547082e97e4434ce1 100644 (file)
@@ -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-09-03  Mark Wielaard  <mark@klomp.org>
 
        * libdw.map (ELFUTILS_0.126): Remove local wildcard.
index 6705294f4c1025ec281342370bbef5f421baed07..a73fb03f73e5deb104e0cb10086b1d7050131ab6 100644 (file)
@@ -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;
 
index 1ebf2dc35816deb44958eb0a8ccc8aa527dd53f0..1b0d712f8b3742039ef70f1f71c090ff513ee994 100644 (file)
@@ -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;
 
index 71ca2baae320db8e1d903343b8ac7bed8b194f0e..98b10437329f14a773bd5451007c3b76134490f7 100644 (file)
@@ -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
index 51932cd98f66f947fe67d803b0e9000d2f92b626..afa8a4607cf69e228323b05e4c2bf0b942ff61f1 100644 (file)
@@ -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;
 
index adcaea03ce4b7f82a18d80f73a0c6c42cc5b1aee..c0e3cadd24894637216422e1ed88515087f9a701 100644 (file)
@@ -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;
index 8a64a15e3fd285a7146ffe21dd70ded3e93c13ce..0313d2a28bbda20574f5ec8564604533012494d9 100644 (file)
@@ -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-09-01  Mark Wielaard  <mark@klomp.org>
 
        * readelf.c (print_debug_ranges_section): Base address entry can
index dd6e28af16783783becd5efa24b71a7a9cd12c1a..64067a57bb68a13f399ff25605205ef44cf2a3c6 100644 (file)
@@ -6182,7 +6182,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)
@@ -6405,8 +6405,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)
@@ -6936,7 +6939,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;
     }
 }
@@ -11100,7 +11103,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;
index 61c6cb7c3db9fdde69a9a2db7714db5c253b09b7..5f2b1449959468a4dfeac9a418ecb684b92f1dd1 100644 (file)
@@ -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.
index 939bbb6a502ef4ce0d9e82cf49649bd7d9c96a83..4629ce64520d504fb746784534075aa69ff93616 100644 (file)
@@ -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 (executable)
index 0000000..f0429d1
--- /dev/null
@@ -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