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

Indu Bhagat indu.bhagat@oracle.com
Tue Jan 28 15:24:52 GMT 2025


On 1/28/25 12:03 AM, 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)
> 
> Both pointer-to-const?
> 

Hmm, the second argument "struct cfi_insn_data *cfi_insn" in all APIs in 
this file can be made const I think. I should do that in a separate 
commit though.  Thanks.

For the first argument, I am not sure yet: I do plan to get to handling 
DW_CFA_GNU_args_size in near future.

>> +{
>> +  int op;
>> +  struct cfi_escape_data *e;
> 
> Again.
> 

OK. Fixed in V2.

>> +  unsigned int reg = 0;
>> +  int err = SFRAME_XLATE_OK;
>> +  struct sframe_row_entry *cur_fre = NULL;
> 
> And yet gain.
> 
> Some of the variables may also better go into the more narrow scope, ...
> 

Sure.

>> +  e = cfi_insn->u.esc;
>> +
>> +  if (e)
> 
> ... or you may want to invert the condition here:
> 
>    if (!e)
>      return ...;
> 
> reducing indentation for the bulk of the function (helping readability).
> 

Fixed in V2.

>> +    {
>> +      op = e->exp.X_add_number;
> 
> You're truncating the value here, and ...
> 

Ah, Thanks. Changed the int op to offsetT op.

>> +      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;
>> +	  /* Keep in sync with the behaviour of cfi_parse_reg ().  */
>> +	  gas_assert (e->exp.X_op == O_register || e->exp.X_op == O_constant);
> 
> ... you're asserting on user input here. Afaics neither is in any way
> enforced by dot_cfi_escape() / do_parse_cons_expression(). As to the
> comment - how does cfi_parse_reg() come into play for .cfi_escape?
> 

IIUC, the first operand of both of these opcodes (DW_CFA_expression, 
DW_CFA_val_expression) will be a register.  Hence, cfi_parse_reg () 
behaviour is being cross-checked against.

This function only aims to process some "simple DWARF expressions", and 
if they are benign (OK to skip), we do not warn (nor error out with 
SFRAME_XLATE_ERR_NOTREPRESENTED).  E.g. for,

DW_CFA_expression: r1 (rdx) (DW_OP_breg7 (rsp): 136)
DW_CFA_expression: r0 (rax) (DW_OP_breg7 (rsp): 144)

>> +	  reg = e->exp.X_add_number;
>> +	  /* Get the scratchpad FRE.  */
>> +	  cur_fre = xlate_ctx->cur_fre;
>> +	  if (reg == SFRAME_CFA_SP_REG || reg == SFRAME_CFA_FP_REG
>> +	      || reg == cur_fre->cfa_base_reg)
>> +	    {
>> +	      as_warn (_("skipping SFrame FDE; .cfi_escape with reg (%#x)"), reg);
>> +	      err = SFRAME_XLATE_ERR_NOTREPRESENTED;
>> +	    }
>> +	  break;
>> +	/* FIXME - Also add processing for DW_CFA_GNU_args_size in future?  */
>> +	default:
>> +	  /* In all other cases (e.g., DW_CFA_def_cfa_expression or other
>> +	     OS-specific CFI opcodes), skip inspecting the DWARF expression.
>> +	     This may impact the asynchronicity due to loss of coverage.
>> +	     Continue to warn the user and bail out.  */
>> +	  as_warn (_("skipping SFrame FDE; .cfi_escape with op (%#x)"), op);
>> +	  err = SFRAME_XLATE_ERR_NOTREPRESENTED;
>> +	  break;
>> +	}
>> +    }
> 
> Doesn't all of this need to go in a loop, as a single .cfi_escape might
> specify multiple successive DW_CFA_*?
> 

The function intends to process only some DWARF expressions (basically 
some commonly-occurring, but safe to skip from SFrame perspective). 
Going into a loop may be necessary for opcodes other than 
DW_CFA_expression or DW_CFA_val_expression, but at the moment, since we 
prefer to cater to only the simple expressions, parsing the complete 
expression is not required.

Thanks


More information about the Binutils mailing list