diff --git a/binutils/NEWS b/binutils/NEWS index 0a4ed3bcc43..c7c44a708d2 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,10 @@ -*- text -*- +* SFrame stack trace format now represents an undefined return address as + an SFrame FRE without any offsets. libsframe provides a new API to test + for RA undefined, which is used when dumping SFrame information (e.g. using + objdump and readelf) to show such FREs as "RA undefined". + * Add --got-contents option to readelf to display the contents of Global Offset Table (GOT) sections. diff --git a/gas/NEWS b/gas/NEWS index 4de0cfbcc91..4bd3d747aa5 100644 --- a/gas/NEWS +++ b/gas/NEWS @@ -1,5 +1,8 @@ -*- text -*- +* Emit an SFrame FRE with zero offsets to convey an undefined return address + in the SFrame stack trace format. + * ELF targets can now have section entity size specified for arbitrary sections, using the new attribute letter 'E'. diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c index 8643ee8731e..b1aa8072410 100644 --- a/gas/gen-sframe.c +++ b/gas/gen-sframe.c @@ -164,6 +164,7 @@ sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset) { fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK; fre->ra_offset = ra_offset; + fre->ra_undefined_p = false; fre->merge_candidate = false; } @@ -511,6 +512,8 @@ sframe_row_entry_new (void) initialize it in sframe_row_entry_initialize () with the sticky bit if set. */ fre->mangled_ra_p = false; + /* Reset the RA undefined status by to zero by default. */ + fre->ra_undefined_p = false; return fre; } @@ -552,6 +555,7 @@ output_sframe_row_entry (symbolS *fde_start_addr, unsigned int fre_num_offsets; unsigned int fre_offset_size; unsigned int fre_base_reg; + bool fre_mangled_ra_p; expressionS exp; unsigned int fre_addr_size; @@ -581,14 +585,30 @@ output_sframe_row_entry (symbolS *fde_start_addr, } /* Create the fre_info using the CFA base register, number of offsets and max - size of offset in this frame row entry. */ - fre_base_reg = get_fre_base_reg_id (sframe_fre); - fre_num_offsets = get_fre_num_offsets (sframe_fre); - fre_offset_size = sframe_get_fre_offset_size (sframe_fre); + size of offset in this frame row entry. Represent RA undefined as FRE + without any offsets and all FRE info word fields zeroed. */ + if (sframe_fre->ra_undefined_p) + { + fre_base_reg = 0; + fre_num_offsets = 0; + fre_offset_size = 0; + fre_mangled_ra_p = 0; + } + else + { + fre_base_reg = get_fre_base_reg_id (sframe_fre); + fre_num_offsets = get_fre_num_offsets (sframe_fre); + fre_offset_size = sframe_get_fre_offset_size (sframe_fre); + fre_mangled_ra_p = sframe_fre->mangled_ra_p; + } fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets, - fre_offset_size, sframe_fre->mangled_ra_p); + fre_offset_size, fre_mangled_ra_p); out_one (fre_info); + /* Represent RA undefined as FRE without any offsets. */ + if (sframe_fre->ra_undefined_p) + return; + idx = sframe_fre_offset_func_map_index (fre_offset_size); gas_assert (idx < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX); @@ -944,6 +964,10 @@ sframe_row_entry_initialize (struct sframe_row_entry *cur_fre, /* Treat RA mangling as a sticky bit. It retains its value until another .cfi_negate_ra_state is seen. */ cur_fre->mangled_ra_p = prev_fre->mangled_ra_p; + /* Treat RA undefined as a sticky bit. It retains its value until a + .cfi_offset RA, .cfi_register RA, .cfi_restore RA, or .cfi_same_value RA + is seen. */ + cur_fre->ra_undefined_p = prev_fre->ra_undefined_p; } /* Return SFrame register name for SP, FP, and RA, or NULL if other. */ @@ -1319,6 +1343,7 @@ sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx, gas_assert (cur_fre); cur_fre->ra_loc = cie_fre->ra_loc; cur_fre->ra_offset = cie_fre->ra_offset; + cur_fre->ra_undefined_p = cie_fre->ra_undefined_p; cur_fre->merge_candidate = false; } return SFRAME_XLATE_OK; @@ -1665,9 +1690,15 @@ sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx, /* Translate DW_CFA_undefined into SFrame context. DW_CFA_undefined op indicates that from now on, the previous value of - register can’t be restored anymore. In SFrame stack trace, we cannot - represent such a semantic. So, we skip generating an SFrame FDE for this, - when a register of interest is used with DW_CFA_undefined. + register can’t be restored anymore. In DWARF, for the return address (RA) + register, this indicates to an unwinder that there is no return address + and the unwind is complete. + + In SFrame, represent the use of the RA register with DW_CFA_undefined as + SFrame FRE without any offsets. Stack tracers can use this as indication + that an outermost frame has been reached and the stack trace is complete. + The use of other registers of interest with DW_CFA_undefined cannot be + represented in SFrame. Therefore skip generating an SFrame FDE. Return SFRAME_XLATE_OK if success. */ @@ -1676,15 +1707,24 @@ sframe_xlate_do_cfi_undefined (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUT const struct cfi_insn_data *cfi_insn) { if (cfi_insn->u.r == SFRAME_CFA_FP_REG - || cfi_insn->u.r == SFRAME_CFA_RA_REG || cfi_insn->u.r == SFRAME_CFA_SP_REG) { as_warn (_("no SFrame FDE emitted; %s reg %u in .cfi_undefined"), sframe_register_name (cfi_insn->u.r), cfi_insn->u.r); return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ } + else if (cfi_insn->u.r == SFRAME_CFA_RA_REG) + { + /* Represent RA undefined (i.e. outermost frame) as FRE without any + offsets. */ + struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; + + gas_assert (cur_fre); + /* Set RA undefined status bit. */ + cur_fre->ra_undefined_p = true; + cur_fre->merge_candidate = false; + } - /* Safe to skip. */ return SFRAME_XLATE_OK; } @@ -1732,6 +1772,7 @@ sframe_xlate_do_same_value (const struct sframe_xlate_ctx *xlate_ctx, { cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_REG; cur_fre->ra_offset = 0; + cur_fre->ra_undefined_p = false; cur_fre->merge_candidate = false; } else if (cfi_insn->u.r == SFRAME_CFA_FP_REG) diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h index 8ad521b5cbe..cf9f5987bf5 100644 --- a/gas/gen-sframe.h +++ b/gas/gen-sframe.h @@ -65,6 +65,9 @@ struct sframe_row_entry /* Whether the return address is mangled with pauth code. */ bool mangled_ra_p; + /* Whether RA is undefined. */ + bool ra_undefined_p; + /* Track CFA base (architectural) register ID. */ unsigned int cfa_base_reg; /* Offset from the CFA base register for recovering CFA. */ diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d new file mode 100644 index 00000000000..f6bd6d71008 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d @@ -0,0 +1,20 @@ +#as: --gsframe +#objdump: --sframe=.sframe +#name: SFrame generation on aarch64 - .cfi_undefined RA +#... +Contents of the SFrame section .sframe: + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_FUNC_START_PCREL + Num FDEs: 1 + Num FREs: 4 + + Function Index : + + func idx \[0\]: pc = 0x0, size = 16 bytes + STARTPC +CFA +FP +RA + + 0+0000 +sp\+0 +u +u + + 0+0004 +sp\+16 +c\-16 +c\-8 + + 0+0008 +RA undefined + 0+000c +RA undefined diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s new file mode 100644 index 00000000000..7b28ab8f652 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s @@ -0,0 +1,13 @@ + .cfi_startproc + stp fp, lr, [sp, #-16]! + .cfi_def_cfa_offset 16 + .cfi_offset 29, -16 + .cfi_offset 30, -8 + nop + .cfi_undefined 30 + ldp fp, lr, [sp], #16 + .cfi_restore 20 + .cfi_restore 19 + .cfi_def_cfa_offset 0 + ret lr + .cfi_endproc diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d new file mode 100644 index 00000000000..dfd47e30bbe --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d @@ -0,0 +1,21 @@ +#name: SFrame generation on s390x - .cfi_undefined RA +#as: --gsframe +#objdump: --sframe=.sframe +#... +Contents of the SFrame section .sframe: + + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_FUNC_START_PCREL + Num FDEs: 1 + Num FREs: 4 + + Function Index : + + func idx \[0\]: pc = 0x0, size = 18 bytes + STARTPC +CFA +FP +RA + + 0+0000 +sp\+160 +u +u + + 0+0006 +sp\+160 +u +c\-48 + + 0+000a +RA undefined + 0+0010 +sp\+160 +u +u + diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s new file mode 100644 index 00000000000..2117a4ca2f0 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s @@ -0,0 +1,11 @@ + .cfi_startproc + stmg %r14,%r15,112(%r15) + .cfi_offset 14, -48 + .cfi_offset 15, -40 + nop + .cfi_undefined 14 + lmg %r14,%r15,112(%r15) + .cfi_restore 15 + .cfi_restore 14 + br %r14 + .cfi_endproc diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d new file mode 100644 index 00000000000..a635c3cd9cb --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d @@ -0,0 +1,22 @@ +#as: --gsframe -O0 +#objdump: --sframe=.sframe +#name: SFrame generation on x86_64 - .cfi_undefined RA +#... +Contents of the SFrame section .sframe: + + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_FUNC_START_PCREL + CFA fixed RA offset: \-8 + Num FDEs: 1 + Num FREs: 4 + + Function Index : + + func idx \[0\]: pc = 0x0, size = 6 bytes + STARTPC +CFA +FP +RA + + 0+0000 +sp\+8 +u +f + + 0+0001 +sp\+16 +c\-16 +f + + 0+0004 +fp\+16 +c\-16 +f + + 0+0005 +RA undefined diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s new file mode 100644 index 00000000000..0f5b9c71715 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s @@ -0,0 +1,11 @@ + .cfi_startproc + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset 6, -16 + movq %rsp, %rbp + .cfi_def_cfa_register 6 + nop + .cfi_undefined 16 + .cfi_def_cfa 7, 8 + ret + .cfi_endproc diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp index 7c93164c4c6..20828516546 100644 --- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp @@ -66,6 +66,7 @@ if { [istarget "x86_64-*-*"] && [gas_sframe_check] } then { run_dump_test "cfi-sframe-x86_64-empty-2" run_dump_test "cfi-sframe-x86_64-empty-3" run_dump_test "cfi-sframe-x86_64-empty-4" + run_dump_test "cfi-sframe-x86_64-ra-undefined-1" set ASFLAGS "$old_ASFLAGS" } } @@ -77,6 +78,7 @@ if { [istarget "aarch64*-*-*"] && [gas_sframe_check] } then { run_dump_test "cfi-sframe-aarch64-3" run_dump_test "cfi-sframe-aarch64-4" run_dump_test "cfi-sframe-aarch64-pac-ab-key-1" + run_dump_test "cfi-sframe-aarch64-ra-undefined-1" } # s390x specific tests @@ -91,4 +93,5 @@ if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then { run_dump_test "cfi-sframe-s390x-fpra-offset-2" run_dump_test "cfi-sframe-s390x-fpra-register-1" run_dump_test "cfi-sframe-s390x-fpra-register-2" + run_dump_test "cfi-sframe-s390x-ra-undefined-1" } diff --git a/include/sframe.h b/include/sframe.h index 7523adbef3a..127bf375057 100644 --- a/include/sframe.h +++ b/include/sframe.h @@ -287,6 +287,7 @@ typedef struct sframe_fre_info #define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf) #define SFRAME_V1_FRE_OFFSET_SIZE(data) (((data) >> 5) & 0x3) #define SFRAME_V1_FRE_MANGLED_RA_P(data) (((data) >> 7) & 0x1) +#define SFRAME_V2_FRE_RA_UNDEFINED_P(data) (SFRAME_V1_FRE_OFFSET_COUNT (data) == 0) /* SFrame Frame Row Entry definitions. diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi index 73060ff8451..689cc28add7 100644 --- a/libsframe/doc/sframe-spec.texi +++ b/libsframe/doc/sframe-spec.texi @@ -171,6 +171,11 @@ by CFA offset alignment factor and then revert CFA offset adjustment). @end itemize @item [Errata 1] An ELF SFrame section has the type SHT_GNU_SFRAME. +@item +[Errata 2] An SFrame FRE info word offset count of zero indicates that the +return address (RA) is undefined for the range of PCs covered by the SFrame FRE. +A stack tracer may use this as indication that an outermost frame has been +reached and the stack trace is complete. @end itemize SFrame version 1 is now obsolete and should not be used. @@ -777,7 +782,10 @@ SFRAME_FRE_OFFSET_4B. @item 1-4 @tab @code{fre_offset_count} @tab A max value of 15 is allowed. Typically, a value of upto 3 is sufficient -for most ABIs to track all three of CFA, FP and RA. +for most ABIs to track all three of CFA, FP and RA. A value of zero indicates +that the return address (RA) is undefined. A stack tracer may use this as +indication that an outermost frame has been reached and the stack trace is +complete. @item 0 @tab @code{fre_cfa_base_reg_id} diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c index d55d3847194..1290966439f 100644 --- a/libsframe/sframe-dump.c +++ b/libsframe/sframe-dump.c @@ -130,6 +130,7 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx, uint64_t func_start_pc_vma = 0; uint64_t fre_start_pc_vma = 0; const char *base_reg_str[] = {"fp", "sp"}; + bool ra_undefined_p = false; int32_t cfa_offset = 0; int32_t fp_offset = 0; int32_t ra_offset = 0; @@ -180,15 +181,25 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx, : func_start_pc_vma + fre.fre_start_addr); /* FIXME - fixup the err caching in array. - assert no error for base reg id. */ + assert no error for base reg id and RA undefined. */ base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]); + ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &fre, &err[0]); cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, &err[0]); fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, &err[1]); ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, &err[2]); - /* Dump CFA info. */ + /* Dump VMA. */ printf ("\n"); printf (" %016"PRIx64, fre_start_pc_vma); + + /* Dump RA undefined (FRE without any offsets). */ + if (ra_undefined_p) + { + printf (" RA undefined"); + continue; + } + + /* Dump CFA info. */ sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset); printf (" %-10s", temp); diff --git a/libsframe/sframe.c b/libsframe/sframe.c index bd39780a913..9d20f2e2d71 100644 --- a/libsframe/sframe.c +++ b/libsframe/sframe.c @@ -134,7 +134,7 @@ sframe_get_fre_ra_mangled_p (uint8_t fre_info) static bool sframe_get_fre_ra_undefined_p (uint8_t fre_info) { - return SFRAME_V1_FRE_OFFSET_COUNT (fre_info) == 0; + return SFRAME_V2_FRE_RA_UNDEFINED_P (fre_info); } /* Access functions for info from function descriptor entry. */ @@ -729,7 +729,8 @@ sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, int8_t fp_offset = sframe_decoder_get_fixed_fp_offset (dctx); /* If the FP offset is not being tracked, return the fixed FP offset from the SFrame header. */ - if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID) + if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID + && !sframe_get_fre_ra_undefined_p (fre->fre_info)) { if (errp) *errp = 0; @@ -760,7 +761,8 @@ sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx, int8_t ra_offset = sframe_decoder_get_fixed_ra_offset (dctx); /* If the RA offset was not being tracked, return the fixed RA offset from the SFrame header. */ - if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID) + if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID + && !sframe_get_fre_ra_undefined_p (fre->fre_info)) { if (errp) *errp = 0;