From: Mark Wielaard Date: Thu, 3 Sep 2020 15:46:53 +0000 (+0200) Subject: libdw,readelf: Recognize DW_CFA_AARCH64_negate_ra_state X-Git-Tag: elfutils-0.181~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5ed73d79bf5670484963cb5b57b8da50c68d5c3e;p=thirdparty%2Felfutils.git libdw,readelf: Recognize DW_CFA_AARCH64_negate_ra_state 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 --- diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 13683ef09..8b0b583a1 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,15 @@ +2020-09-03 Mark Wielaard + + * 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 * libdw.map (ELFUTILS_0.126): Remove local wildcard. diff --git a/libdw/cfi.c b/libdw/cfi.c index 6705294f4..a73fb03f7 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 1ebf2dc35..1b0d712f8 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 71ca2baae..98b104373 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 51932cd98..afa8a4607 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 adcaea03c..c0e3cadd2 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 8a64a15e3..0313d2a28 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2020-09-03 Mark Wielaard + + * 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 * readelf.c (print_debug_ranges_section): Base address entry can diff --git a/src/readelf.c b/src/readelf.c index dd6e28af1..64067a57b 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -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; diff --git a/tests/ChangeLog b/tests/ChangeLog index 61c6cb7c3..5f2b14499 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2020-09-03 Mark Wielaard + + * run-readelf-frames.sh: New test. + * Makefile.am (TESTS): Add run-readelf-frames.sh. + (EXTRA_DIST): Likewise. + 2020-09-03 Mark Wielaard * testfile-gnu-property-note-aarch64.bz2: New file. diff --git a/tests/Makefile.am b/tests/Makefile.am index 939bbb6a5..4629ce645 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 000000000..f0429d19e --- /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 . + +. $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