[PATCH] gas: sframe: partially process DWARF expressions in CFI_escape

Indu Bhagat indu.bhagat@oracle.com
Wed Jan 29 20:23:20 GMT 2025


On 1/28/25 11:22 PM, Jan Beulich wrote:
> 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 ...
> 

We can know that e is non-NULL because we are doing this only for 
DW_CFA_expression and DW_CFA_val_expression.

The DWARF standard says:

"The DW_CFA_expression instruction takes two operands: an unsigned
LEB128 value representing a register number, and a DW_FORM_block value
representing a DWARF expression."

Similar specification for DW_CFA_val_expression.

So the code is assuming that after the op (DW_CFA_expression or 
DW_CFA_val_expression), the next one up is the register.  I think 
assuming next one is the register is also OK to do because the DWARF 
expression evaluation is based on a stack machine model.

>> +	  /* 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.
> 

We have checked that the op is DW_CFA_expression or 
DW_CFA_val_expression at this point...

For all other DW_CFA_* in the .cfi_escape, we do not decipher them, and 
simply warn and error out (and not generate any SFrame FDE for that 
function).

> 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?
> 

advance_loc, remember/restore state in .cfi_escape are important for 
SFrame stack trace data generation.  GAS will continue to warn and error 
out with:
   skipping SFrame FDE; .cfi_escape with op (XXX)

But to your point, surely, there may be more "harmless" cases when it is 
better to scan the .cfi_escape data and if it doesnt affect SFrame stack 
trace data, we do not warn.  My intention is to take them on a 
case-by-case basis: target the easy-to-parse/process and commonly 
occurring ones first.


More information about the Binutils mailing list