[PATCH] gas: sframe: partially process DWARF expressions in CFI_escape
Jan Beulich
jbeulich@suse.com
Wed Jan 29 07:22:49 GMT 2025
On 28.01.2025 01:57, Indu Bhagat wrote:
> --- a/gas/gen-sframe.c
> +++ b/gas/gen-sframe.c
> @@ -1310,6 +1310,77 @@ sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx,
> return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
> }
>
> +/* Translate CFI_escape into SFrame context.
> +
> + .cfi_escape CFI directive allows the user to add arbitrary bytes to the
> + unwind info. DWARF expressions commonly follow after CFI_escape (fake CFI)
> + DWARF opcode. One might also use CFI_escape to add OS-specific CFI opcodes
> + even.
> +
> + In SFrame stack trace format, complex unwind info cannot be represented. In
> + such cases, SFrame FDE generation is skipped and the user is warned. Recall,
> + however, that SFrame stack trace information is meant to convey information
> + about SP, FP and RA only. Hence, some DWARF expressions, are indeed safe to
> + skip.
> +
> + This function partially processes some DWARF expresssions and returns
> + SFRAME_XLATE_OK if OK to skip. */
> +
> +static int
> +sframe_xlate_do_expr (struct sframe_xlate_ctx *xlate_ctx,
> + struct cfi_insn_data *cfi_insn)
> +{
> + int op;
> + struct cfi_escape_data *e;
> + unsigned int reg = 0;
> + int err = SFRAME_XLATE_OK;
> + struct sframe_row_entry *cur_fre = NULL;
> +
> + e = cfi_insn->u.esc;
> +
> + if (e)
> + {
> + op = e->exp.X_add_number;
> + switch (op)
> + {
> + /* Of all the possible opcodes expected here, it is safe to
> + ignore DW_CFA_expression and DW_CFA_val_expression, provided they
> + do not impact the SP / FP register. */
> + case DW_CFA_expression:
> + case DW_CFA_val_expression:
> + /* Both DW_CFA_expression and DW_CFA_val_expression instructions take
> + two operands: an unsigned LEB128 value representing a register
> + number, and a DW_FORM_block value representing a DWARF expression.
> + For the current purpose, we simply need to know the register
> + number. */
> + e = e->next;
Another thing (quite the opposite of my concern regarding the chain being
quite long): How do you know e is non-NULL at this point, i.e. that it is
safe to de-reference ...
> + /* Keep in sync with the behaviour of cfi_parse_reg (). */
> + gas_assert (e->exp.X_op == O_register || e->exp.X_op == O_constant);
> + reg = e->exp.X_add_number;
... without checking? Much like a single .cfi_escape can comprise many
DW_CFA_*, a single DW_CFA_* can also be split across multiple
.cfi_escape, aiui.
Consider this example covering both of the named cases (without involving
any DW_CFA_*expression, just to demonstrate the possible uses of the
directive):
.text
func:
.cfi_startproc
.cfi_escape 0x0a
nop
.cfi_escape 0x02, 0x00, 0x02, 0x00
nop
.cfi_escape 0x03
.cfi_escape 0x00
.cfi_escape 0x00
nop
.cfi_escape 0x0b
ret
.cfi_endproc
Also what about in particular operations that don't affect any registers
(other than perhaps PC)? DW_CFA_nop being the most prominent example, but
also any purely advance-loc ones (and others, like remember/restore state).
Wouldn't you better skip those before deciding whether to warn?
Jan
More information about the Binutils
mailing list