diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c index f5cf9bd8594..ac1427856fa 100644 --- a/gas/gen-sframe.c +++ b/gas/gen-sframe.c @@ -1510,6 +1510,64 @@ warn_and_exit: return err; } +/* Handle DW_CFA_GNU_args_size in .cfi_escape. + + The purpose of DW_CFA_GNU_args_size is to adjust SP when performing stack + unwinding for exception handling. For stack tracing needs, + DW_CFA_GNU_args_size can be ignored, when CFA is FP-based. This is because + if the topmost frame is that of the catch block, the SP has been restored to + correct value by exception handling logic. From this point of interest in + the catch block now, stack tracing intends to go backwards to the caller + frame. If CFA restoration does not need SP, DW_CFA_GNU_args_size can be + ignored for stack tracing. + + Continue to warn and not emit SFrame FDE if CFA is SP based. The pattern + where the CFA is SP based and there is a DW_CFA_GNU_args_size for + SP-adjustment is not entirely clear. + + Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the + caller must warn. The caller then must also set + SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ + +static int +sframe_xlate_do_escape_gnu_args_size (const struct sframe_xlate_ctx *xlate_ctx, + const struct cfi_insn_data *cfi_insn, + bool *caller_warn_p) +{ + const struct cfi_escape_data *e = cfi_insn->u.esc; + unsigned int i = 0; + + /* Check for (DW_CFA_GNU_args_size offset) sequence. */ +#define CFI_ESC_NUM_EXP 1 + offsetT items[CFI_ESC_NUM_EXP] = {0}; + while (e->next) + { + e = e->next; + if (i >= CFI_ESC_NUM_EXP || e->exp.X_op != O_constant + || e->type != CFI_ESC_byte + || e->reloc != TC_PARSE_CONS_RETURN_NONE) + goto warn_and_exit; + items[i] = e->exp.X_add_number; + i++; + } + if (i == 0) + goto warn_and_exit; + +#undef CFI_ESC_NUM_EXP + + offsetT offset = items[0]; + + struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; + gas_assert (cur_fre); + /* If CFA is FP based, safe to skip. */ + if (offset == 0 || cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG) + return SFRAME_XLATE_OK; + +warn_and_exit: + *caller_warn_p = true; + return SFRAME_XLATE_OK; +} + /* Handle CFI_escape in SFrame context. .cfi_escape CFI directive allows the user to add arbitrary data to the @@ -1573,7 +1631,9 @@ sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx, err = sframe_xlate_do_escape_val_offset (xlate_ctx, cfi_insn, &warn_p); break; - /* FIXME - Also add processing for DW_CFA_GNU_args_size in future? */ + case DW_CFA_GNU_args_size: + err = sframe_xlate_do_escape_gnu_args_size (xlate_ctx, cfi_insn, &warn_p); + break; default: warn_p = true; diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d new file mode 100644 index 00000000000..c91ce7b1011 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d @@ -0,0 +1,22 @@ +#as: --gsframe +#objdump: --sframe=.sframe +#name: SFrame DW_CFA_GNU_args_size test +#... +Contents of the SFrame section .sframe: + + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_FUNC_START_PCREL +#? CFA fixed FP offset: \-?\d+ +#? CFA fixed RA offset: \-?\d+ + Num FDEs: 1 + Num FREs: 2 + + Function Index : + func idx \[0\]: pc = 0x0, size = 8 bytes + STARTPC + CFA + FP + RA + +#... + 0+0004 +sp\+16 +u +[uf] + + +#pass diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s new file mode 100644 index 00000000000..41af4e11b2e --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s @@ -0,0 +1,8 @@ +## DW_CFA_GNU_args_size 0 can be ignored + .cfi_startproc + .long 0 + .cfi_def_cfa_offset 16 + .long + .cfi_escape 0x2e, 0x0 + .long 0 + .cfi_endproc diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d new file mode 100644 index 00000000000..0e495159c01 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d @@ -0,0 +1,21 @@ +#as: --gsframe +#objdump: --sframe=.sframe +#name: CFI_escape DW_CFA_GNU_args_size with FP-based CFA +#... +Contents of the SFrame section .sframe: + + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_FUNC_START_PCREL +#? CFA fixed FP offset: \-?\d+ +#? CFA fixed RA offset: \-?\d+ + Num FDEs: 1 + Num FREs: 1 + + Function Index : + + func idx \[0\]: pc = 0x0, size = 0 bytes + STARTPC +CFA +FP +RA + + 0+0000 +fp\+8 +u +f + +#pass diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s new file mode 100644 index 00000000000..e28efefb236 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s @@ -0,0 +1,5 @@ +# DW_CFA_GNU_args_size is safe to skip, if CFA is FP based. + .cfi_startproc + .cfi_def_cfa_register 6 + .cfi_escape 0x2e, 0x20 + .cfi_endproc diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp index 9380c98fc3d..7c93164c4c6 100644 --- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp @@ -47,6 +47,7 @@ if { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"] run_dump_test "cfi-sframe-common-9" run_dump_test "cfi-sframe-common-10" run_dump_test "cfi-sframe-common-11" + run_dump_test "cfi-sframe-common-12" run_dump_test "common-empty-1" run_dump_test "common-empty-2" @@ -59,6 +60,7 @@ if { [istarget "x86_64-*-*"] && [gas_sframe_check] } then { set ASFLAGS "$ASFLAGS --64" run_dump_test "cfi-sframe-x86_64-1" run_dump_test "cfi-sframe-x86_64-2" + run_dump_test "cfi-sframe-x86_64-3" run_dump_test "cfi-sframe-x86_64-pr33170" run_dump_test "cfi-sframe-x86_64-empty-1" run_dump_test "cfi-sframe-x86_64-empty-2"