[7/8] gas: permit LEB128 operands for .cfi_escape

Message ID be5682bf-d3fe-4119-91e4-3af0ad38ae6e@suse.com
State New
Headers
Series gas: CFI directive and listing adjustments |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Jan Beulich Feb. 26, 2025, 3:18 p.m. UTC
  Many DW_CFA_* and DW_OP_* take LEB128 operands. Having to hand-encode
such when needing to resort to .cfi_escape isn't very helpful.
  

Comments

Indu Bhagat March 6, 2025, 2:35 p.m. UTC | #1
On 2/26/25 7:18 AM, Jan Beulich wrote:
> Many DW_CFA_* and DW_OP_* take LEB128 operands. Having to hand-encode
> such when needing to resort to .cfi_escape isn't very helpful.
> 
> --- a/gas/doc/as.texi
> +++ b/gas/doc/as.texi
> @@ -5176,7 +5176,18 @@ SPARC register window has been saved.
>   @subsection @code{.cfi_escape} @var{expression}[, @dots{}]
>   Allows the user to add arbitrary bytes to the unwind info.  One
>   might use this to add OS-specific CFI opcodes, or generic CFI
> -opcodes that GAS does not yet support.
> +opcodes that GAS does not yet support.  To emit multi-byte data one may
> +also use extended kind-of-expression forms:
> +
> +@itemize @bullet
> +
> +@item @code{sleb128(@var{expression})}
> +to emit a SLEB128 item,
> +
> +@item @code{uleb128(@var{expression})}
> +to emit a SLEB128 item.
> +

Typo: ULEB128 item.

> +@end itemize
>   
>   @subsection @code{.cfi_val_encoded_addr @var{register}, @var{encoding}, @var{label}}
>   The current value of @var{register} is @var{label}.  The value of @var{label}
> --- a/gas/dw2gencfi.c
> +++ b/gas/dw2gencfi.c
> @@ -372,6 +372,11 @@ struct cfi_escape_data
>   {
>     struct cfi_escape_data *next;
>     expressionS exp;
> +  enum {
> +    ESC_byte,
> +    ESC_sleb128,
> +    ESC_uleb128,
> +  } type;
>     TC_PARSE_CONS_RETURN_TYPE reloc;
>   };
>   
> @@ -948,7 +953,7 @@ dot_cfi (int arg)
>   static void
>   dot_cfi_escape (int ignored ATTRIBUTE_UNUSED)
>   {
> -  struct cfi_escape_data *head, **tail, *e;
> +  struct cfi_escape_data *head, **tail;
>     struct cfi_insn_data *insn;
>   
>     if (frchain_now->frch_cfi_data == NULL)
> @@ -967,8 +972,39 @@ dot_cfi_escape (int ignored ATTRIBUTE_UN
>     tail = &head;
>     do
>       {
> -      e = notes_alloc (sizeof (*e));
> -      e->reloc = do_parse_cons_expression (&e->exp, 1);
> +      struct cfi_escape_data *e = notes_alloc (sizeof (*e));
> +      char *id, *ilp_save = input_line_pointer;
> +      char c = get_symbol_name (&id);
> +
> +      if (strcmp (id, "sleb128") == 0)
> +	e->type = ESC_sleb128;
> +      else if (strcmp (id, "uleb128") == 0)
> +	e->type = ESC_uleb128;
> +      else
> +	e->type = ESC_byte;
> +
> +      c = restore_line_pointer (c);
> +
> +      if (e->type != ESC_byte)
> +	{
> +	  if (is_whitespace (c))
> +	    c = *++input_line_pointer;
> +	  if (c != '(')
> +	    {
> +	      input_line_pointer = ilp_save;
> +	      e->type = ESC_byte;
> +	    }
> +	}
> +
> +      if (e->type == ESC_sleb128 || e->type == ESC_uleb128)
> +	{
> +	  /* We're still at the opening parenthesis.  Leave it to expression()
> +	     to parse it and find the matching closing one.  */
> +	  expression (&e->exp);
> +	}
> +      else
> +	e->reloc = do_parse_cons_expression (&e->exp, 1);
> +
>         *tail = e;
>         tail = &e->next;
>       }
> @@ -1785,7 +1821,12 @@ output_cfi_insn (struct cfi_insn_data *i
>         {
>   	struct cfi_escape_data *e;
>   	for (e = insn->u.esc; e ; e = e->next)
> -	  emit_expr_with_reloc (&e->exp, 1, e->reloc);
> +	  {
> +	    if (e->type == ESC_sleb128 || e->type == ESC_uleb128)
> +	      emit_leb128_expr (&e->exp, e->type == ESC_sleb128);
> +	    else
> +	      emit_expr_with_reloc (&e->exp, 1, e->reloc);
> +	  }
>   	break;
>         }
>   
> --- a/gas/testsuite/gas/cfi/listing.l
> +++ b/gas/testsuite/gas/cfi/listing.l
> @@ -20,8 +20,20 @@
>   [ 	]*[0-9]*[ 	]+([0-9a-f]{4} [0 	]+)?\.cfi_endproc
>   [ 	]*[0-9]*[ 	]*
>   [ 	]*[0-9]*[ 	]+func2:
> -[ 	]*[0-9]*[ 	]+[0-9a-f]{4} (1[048]00 ?0000|0000 ?001[048])[ 	]+\.cfi_startproc
> +[ 	]*[0-9]*[ 	]+[0-9a-f]{4} ((1C|2[048])00 ?0000|0000 ?00(1C|2[048]))[ 	]+\.cfi_startproc
>   [ 	]*[0-9]*[ 	]+(4[048]00 ?0000|0000 ?004[048]) *
>   [ 	]*[0-9]*[ 	]+[0-9a-f]{4} .*[ 	]\.nop
> +[ 	]*[0-9]*[ 	]+/\* DW_CFA_register reg127, reg129.  \*/
> +[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 4.09 ?7F81[ 	]+\.cfi_escape 0x09, uleb128\(127\), uleb128\(129\)
> +[ 	]*[0-9]*[ 	]+01
> +[ 	]*[0-9]*[ 	]+/\* DW_CFA_val_expression reg250, ...  \*/
> +[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 16FA ?01[ 	]+\.cfi_escape 0x16, uleb128\(250\)
> +[ 	]*[0-9]*[ 	]+/\* ... <len>.  \*/
> +[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 03[ 	]+\.cfi_escape uleb128\(.LE0e - .LE0s\)
> +[ 	]*[0-9]*[ 	]+\.cfi_label .LE0s
> +[ 	]*[0-9]*[ 	]+/\* DW_OP_breg3.  \*/
> +[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 7380 ?78[ 	]+\.cfi_escape 0x73, sleb128\(-1024\)
> +[ 	]*[0-9]*[ 	]+\.cfi_label .LE0e
> +[ 	]*[0-9]*[ 	]+[0-9a-f]{4} .*[ 	]\.nop
>   [ 	]*[0-9]*[ 	]+([0-9a-f]{4} [0 	]+)?\.cfi_endproc
>   #pass
> --- a/gas/testsuite/gas/cfi/listing.s
> +++ b/gas/testsuite/gas/cfi/listing.s
> @@ -19,4 +19,15 @@ func:
>   func2:
>   	.cfi_startproc
>   	.nop
> +	/* DW_CFA_register reg127, reg129.  */
> +	.cfi_escape 0x09, uleb128(127), uleb128(129)
> +	/* DW_CFA_val_expression reg250, ...  */
> +	.cfi_escape 0x16, uleb128(250)
> +	/* ... <len>.  */
> +	.cfi_escape uleb128(.LE0e - .LE0s)
> +	.cfi_label .LE0s
> +	/* DW_OP_breg3.  */
> +	.cfi_escape 0x73, sleb128(-1024)
> +	.cfi_label .LE0e
> +	.nop
>   	.cfi_endproc
>
  

Patch

--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -5176,7 +5176,18 @@  SPARC register window has been saved.
 @subsection @code{.cfi_escape} @var{expression}[, @dots{}]
 Allows the user to add arbitrary bytes to the unwind info.  One
 might use this to add OS-specific CFI opcodes, or generic CFI
-opcodes that GAS does not yet support.
+opcodes that GAS does not yet support.  To emit multi-byte data one may
+also use extended kind-of-expression forms:
+
+@itemize @bullet
+
+@item @code{sleb128(@var{expression})}
+to emit a SLEB128 item,
+
+@item @code{uleb128(@var{expression})}
+to emit a SLEB128 item.
+
+@end itemize
 
 @subsection @code{.cfi_val_encoded_addr @var{register}, @var{encoding}, @var{label}}
 The current value of @var{register} is @var{label}.  The value of @var{label}
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -372,6 +372,11 @@  struct cfi_escape_data
 {
   struct cfi_escape_data *next;
   expressionS exp;
+  enum {
+    ESC_byte,
+    ESC_sleb128,
+    ESC_uleb128,
+  } type;
   TC_PARSE_CONS_RETURN_TYPE reloc;
 };
 
@@ -948,7 +953,7 @@  dot_cfi (int arg)
 static void
 dot_cfi_escape (int ignored ATTRIBUTE_UNUSED)
 {
-  struct cfi_escape_data *head, **tail, *e;
+  struct cfi_escape_data *head, **tail;
   struct cfi_insn_data *insn;
 
   if (frchain_now->frch_cfi_data == NULL)
@@ -967,8 +972,39 @@  dot_cfi_escape (int ignored ATTRIBUTE_UN
   tail = &head;
   do
     {
-      e = notes_alloc (sizeof (*e));
-      e->reloc = do_parse_cons_expression (&e->exp, 1);
+      struct cfi_escape_data *e = notes_alloc (sizeof (*e));
+      char *id, *ilp_save = input_line_pointer;
+      char c = get_symbol_name (&id);
+
+      if (strcmp (id, "sleb128") == 0)
+	e->type = ESC_sleb128;
+      else if (strcmp (id, "uleb128") == 0)
+	e->type = ESC_uleb128;
+      else
+	e->type = ESC_byte;
+
+      c = restore_line_pointer (c);
+
+      if (e->type != ESC_byte)
+	{
+	  if (is_whitespace (c))
+	    c = *++input_line_pointer;
+	  if (c != '(')
+	    {
+	      input_line_pointer = ilp_save;
+	      e->type = ESC_byte;
+	    }
+	}
+
+      if (e->type == ESC_sleb128 || e->type == ESC_uleb128)
+	{
+	  /* We're still at the opening parenthesis.  Leave it to expression()
+	     to parse it and find the matching closing one.  */
+	  expression (&e->exp);
+	}
+      else
+	e->reloc = do_parse_cons_expression (&e->exp, 1);
+
       *tail = e;
       tail = &e->next;
     }
@@ -1785,7 +1821,12 @@  output_cfi_insn (struct cfi_insn_data *i
       {
 	struct cfi_escape_data *e;
 	for (e = insn->u.esc; e ; e = e->next)
-	  emit_expr_with_reloc (&e->exp, 1, e->reloc);
+	  {
+	    if (e->type == ESC_sleb128 || e->type == ESC_uleb128)
+	      emit_leb128_expr (&e->exp, e->type == ESC_sleb128);
+	    else
+	      emit_expr_with_reloc (&e->exp, 1, e->reloc);
+	  }
 	break;
       }
 
--- a/gas/testsuite/gas/cfi/listing.l
+++ b/gas/testsuite/gas/cfi/listing.l
@@ -20,8 +20,20 @@ 
 [ 	]*[0-9]*[ 	]+([0-9a-f]{4} [0 	]+)?\.cfi_endproc
 [ 	]*[0-9]*[ 	]*
 [ 	]*[0-9]*[ 	]+func2:
-[ 	]*[0-9]*[ 	]+[0-9a-f]{4} (1[048]00 ?0000|0000 ?001[048])[ 	]+\.cfi_startproc
+[ 	]*[0-9]*[ 	]+[0-9a-f]{4} ((1C|2[048])00 ?0000|0000 ?00(1C|2[048]))[ 	]+\.cfi_startproc
 [ 	]*[0-9]*[ 	]+(4[048]00 ?0000|0000 ?004[048]) *
 [ 	]*[0-9]*[ 	]+[0-9a-f]{4} .*[ 	]\.nop
+[ 	]*[0-9]*[ 	]+/\* DW_CFA_register reg127, reg129.  \*/
+[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 4.09 ?7F81[ 	]+\.cfi_escape 0x09, uleb128\(127\), uleb128\(129\)
+[ 	]*[0-9]*[ 	]+01
+[ 	]*[0-9]*[ 	]+/\* DW_CFA_val_expression reg250, ...  \*/
+[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 16FA ?01[ 	]+\.cfi_escape 0x16, uleb128\(250\)
+[ 	]*[0-9]*[ 	]+/\* ... <len>.  \*/
+[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 03[ 	]+\.cfi_escape uleb128\(.LE0e - .LE0s\)
+[ 	]*[0-9]*[ 	]+\.cfi_label .LE0s
+[ 	]*[0-9]*[ 	]+/\* DW_OP_breg3.  \*/
+[ 	]*[0-9]*[ 	]+[0-9a-f]{4} 7380 ?78[ 	]+\.cfi_escape 0x73, sleb128\(-1024\)
+[ 	]*[0-9]*[ 	]+\.cfi_label .LE0e
+[ 	]*[0-9]*[ 	]+[0-9a-f]{4} .*[ 	]\.nop
 [ 	]*[0-9]*[ 	]+([0-9a-f]{4} [0 	]+)?\.cfi_endproc
 #pass
--- a/gas/testsuite/gas/cfi/listing.s
+++ b/gas/testsuite/gas/cfi/listing.s
@@ -19,4 +19,15 @@  func:
 func2:
 	.cfi_startproc
 	.nop
+	/* DW_CFA_register reg127, reg129.  */
+	.cfi_escape 0x09, uleb128(127), uleb128(129)
+	/* DW_CFA_val_expression reg250, ...  */
+	.cfi_escape 0x16, uleb128(250)
+	/* ... <len>.  */
+	.cfi_escape uleb128(.LE0e - .LE0s)
+	.cfi_label .LE0s
+	/* DW_OP_breg3.  */
+	.cfi_escape 0x73, sleb128(-1024)
+	.cfi_label .LE0e
+	.nop
 	.cfi_endproc