This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Fwd: Add DW_OP_GNU_encoded_addr]


Here's the gas/binutils part of the feature I just posted about
on the gcc list.


r~



-------- Original Message -------- Subject: Add DW_OP_GNU_encoded_addr Date: Wed, 24 Sep 2008 16:14:18 -0700 From: Richard Henderson <rth@redhat.com> To: gcc-patches@gcc.gnu.org

This is a new dwarf unwind expression opcode that allows an absolute
address to be put into the .eh_frame section without having to resort
to DW_OP_addr, which would result in runtime relocations in shared
libraries.

This opcode will be generated by a new gas .cfi_val_encoded_addr
directive.  This directive is intended to be used with inline assembly
which actually contains out-of-line code (i.e. in another section).
E.g.

   asm(	"test something
	jnz 1f
	.section text2,\"ax\"
1:	.cfi_startproc simple
	.cfi_def_cfa %%esp, 0
	.cfi_val_encoded_addr %%eip, 0x1b, 11f
	<do-some-stuff>
	jmp 11f
	.cfi_endproc
	.previous
11:");

This is an idiom that shows up several places in glibc and in the
kernel.  In glibc, Jakub currently goes to heroic efforts to
generate the unwind information by hand, but this code is what you
might call write-only i.e. intensely difficult to modify.


r~


include/
        * elf/dwarf2.h (DW_OP_GNU_encoded_addr): New.
binutils/
        * dwarf.c (size_of_encoded_value, get_encoded_value): Move up.
        (decode_location_expression): Add section parameter.  Handle
        DW_OP_GNU_encoded_addr.
        (read_and_display_attr_value): Update decode_location_expression call.
        (display_debug_loc, display_debug_frames): Likewise.
gas/
        * dw2gencfi.c (DWARF2_ADDR_SIZE): Provide default.
        (struct cfi_insn_data): Add ea member.
        (CFI_val_encoded_addr, dot_cfi_val_encoded_addr): New.
        (output_cfi_insn): Handle CFI_val_encoded_addr.
        (select_cie_for_fde): Don't match CFI_val_encoded_addr.
        * doc/as.texinfo (.cfi_val_encoded_addr): Document.

Index: binutils/dwarf.c
===================================================================
RCS file: /cvs/src/src/binutils/dwarf.c,v
retrieving revision 1.39
diff -u -p -r1.39 dwarf.c
--- binutils/dwarf.c	24 Sep 2008 14:37:35 -0000	1.39
+++ binutils/dwarf.c	24 Sep 2008 22:26:32 -0000
@@ -164,6 +164,30 @@ byte_get_signed (unsigned char *field, i
     }
 }
 
+static int
+size_of_encoded_value (int encoding)
+{
+  switch (encoding & 0x7)
+    {
+    default:	/* ??? */
+    case 0:	return eh_addr_size;
+    case 2:	return 2;
+    case 3:	return 4;
+    case 4:	return 8;
+    }
+}
+
+static dwarf_vma
+get_encoded_value (unsigned char *data, int encoding)
+{
+  int size = size_of_encoded_value (encoding);
+
+  if (encoding & DW_EH_PE_signed)
+    return byte_get_signed (data, size);
+  else
+    return byte_get (data, size);
+}
+
 /* Print a dwarf_vma value (typically an address, offset or length) in
    hexadecimal format, followed by a space.  The length of the value (and
    hence the precision displayed) is determined by the byte_size parameter.  */
@@ -651,7 +675,8 @@ static int
 decode_location_expression (unsigned char * data,
 			    unsigned int pointer_size,
 			    unsigned long length,
-			    unsigned long cu_offset)
+			    unsigned long cu_offset,
+			    struct dwarf_section * section)
 {
   unsigned op;
   unsigned int bytes_read;
@@ -989,6 +1014,21 @@ decode_location_expression (unsigned cha
 	  printf ("DW_OP_GNU_uninit");
 	  /* FIXME: Is there data associated with this OP ?  */
 	  break;
+	case DW_OP_GNU_encoded_addr:
+	  {
+	    int encoding;
+	    dwarf_vma addr;
+	
+	    encoding = *data++;
+	    addr = get_encoded_value (data, encoding);
+	    if ((encoding & 0x70) == DW_EH_PE_pcrel)
+	      addr += section->address + (data - section->start);
+	    data += size_of_encoded_value (encoding);
+
+	    printf ("DW_OP_GNU_encoded_addr: fmt:%02x addr:", encoding);
+	    print_dwarf_vma (addr, pointer_size);
+	  }
+	  break;
 
 	  /* HP extensions.  */
 	case DW_OP_HP_is_value:
@@ -1508,7 +1548,7 @@ read_and_display_attr_value (unsigned lo
 	  need_frame_base = decode_location_expression (block_start,
 							pointer_size,
 							uvalue,
-							cu_offset);
+							cu_offset, section);
 	  printf (")");
 	  if (need_frame_base && !have_frame_base)
 	    printf (_(" [without DW_AT_frame_base]"));
@@ -3186,7 +3226,7 @@ display_debug_loc (struct dwarf_section 
 	      need_frame_base = decode_location_expression (start,
 							    pointer_size,
 							    length,
-							    cu_offset);
+							    cu_offset, section);
 	      putchar (')');
 
 	      if (need_frame_base && !has_frame_base)
@@ -3756,30 +3796,6 @@ frame_display_row (Frame_Chunk *fc, int 
   printf ("\n");
 }
 
-static int
-size_of_encoded_value (int encoding)
-{
-  switch (encoding & 0x7)
-    {
-    default:	/* ??? */
-    case 0:	return eh_addr_size;
-    case 2:	return 2;
-    case 3:	return 4;
-    case 4:	return 8;
-    }
-}
-
-static dwarf_vma
-get_encoded_value (unsigned char *data, int encoding)
-{
-  int size = size_of_encoded_value (encoding);
-
-  if (encoding & DW_EH_PE_signed)
-    return byte_get_signed (data, size);
-  else
-    return byte_get (data, size);
-}
-
 #define GET(N)	byte_get (start, N); start += N
 #define LEB()	read_leb128 (start, & length_return, 0); start += length_return
 #define SLEB()	read_leb128 (start, & length_return, 1); start += length_return
@@ -4379,7 +4395,8 @@ display_debug_frames (struct dwarf_secti
 	      if (! do_debug_frames_interp)
 		{
 		  printf ("  DW_CFA_def_cfa_expression (");
-		  decode_location_expression (start, eh_addr_size, ul, 0);
+		  decode_location_expression (start, eh_addr_size, ul, 0,
+					      section);
 		  printf (")\n");
 		}
 	      fc->cfa_exp = 1;
@@ -4394,7 +4411,7 @@ display_debug_frames (struct dwarf_secti
 		  printf ("  DW_CFA_expression: %s (",
 			  regname (reg, 0));
 		  decode_location_expression (start, eh_addr_size,
-					      ul, 0);
+					      ul, 0, section);
 		  printf (")\n");
 		}
 	      fc->col_type[reg] = DW_CFA_expression;
@@ -4408,7 +4425,8 @@ display_debug_frames (struct dwarf_secti
 		{
 		  printf ("  DW_CFA_val_expression: %s (",
 			  regname (reg, 0));
-		  decode_location_expression (start, eh_addr_size, ul, 0);
+		  decode_location_expression (start, eh_addr_size, ul, 0,
+					      section);
 		  printf (")\n");
 		}
 	      fc->col_type[reg] = DW_CFA_val_expression;
Index: gas/dw2gencfi.c
===================================================================
RCS file: /cvs/src/src/gas/dw2gencfi.c,v
retrieving revision 1.36
diff -u -p -r1.36 dw2gencfi.c
--- gas/dw2gencfi.c	7 Sep 2008 22:54:53 -0000	1.36
+++ gas/dw2gencfi.c	24 Sep 2008 22:26:33 -0000
@@ -58,6 +58,10 @@
 # define tc_cfi_frame_initial_instructions() ((void)0)
 #endif
 
+#ifndef DWARF2_ADDR_SIZE
+# define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
+#endif
+
 
 struct cfi_insn_data
 {
@@ -86,6 +90,11 @@ struct cfi_insn_data
       struct cfi_escape_data *next;
       expressionS exp;
     } *esc;
+
+    struct {
+      unsigned reg, encoding;
+      expressionS exp;
+    } ea;
   } u;
 };
 
@@ -376,6 +385,7 @@ static void dot_cfi_startproc (int);
 static void dot_cfi_endproc (int);
 static void dot_cfi_personality (int);
 static void dot_cfi_lsda (int);
+static void dot_cfi_val_encoded_addr (int);
 
 /* Fake CFI type; outside the byte range of any real CFI insn.  */
 #define CFI_adjust_cfa_offset	0x100
@@ -383,6 +393,7 @@ static void dot_cfi_lsda (int);
 #define CFI_rel_offset		0x102
 #define CFI_escape		0x103
 #define CFI_signal_frame	0x104
+#define CFI_val_encoded_addr	0x105
 
 const pseudo_typeS cfi_pseudo_table[] =
   {
@@ -406,6 +417,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_signal_frame", dot_cfi, CFI_signal_frame },
     { "cfi_personality", dot_cfi_personality, 0 },
     { "cfi_lsda", dot_cfi_lsda, 0 },
+    { "cfi_val_encoded_addr", dot_cfi_val_encoded_addr, 0 },
     { NULL, NULL, 0 }
   };
 
@@ -654,7 +666,7 @@ dot_cfi_personality (int ignored ATTRIBU
     }
 
   fde = frchain_now->frch_cfi_data->cur_fde_data;
-  encoding = get_absolute_expression ();
+  encoding = cfi_parse_const ();
   if (encoding == DW_EH_PE_omit)
     {
       demand_empty_rest_of_line ();
@@ -724,7 +736,7 @@ dot_cfi_lsda (int ignored ATTRIBUTE_UNUS
     }
 
   fde = frchain_now->frch_cfi_data->cur_fde_data;
-  encoding = get_absolute_expression ();
+  encoding = cfi_parse_const ();
   if (encoding == DW_EH_PE_omit)
     {
       demand_empty_rest_of_line ();
@@ -783,6 +795,71 @@ dot_cfi_lsda (int ignored ATTRIBUTE_UNUS
 }
 
 static void
+dot_cfi_val_encoded_addr (int ignored ATTRIBUTE_UNUSED)
+{
+  struct cfi_insn_data *insn_ptr;
+  offsetT encoding;
+
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_("CFI instruction used without previous .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* If the last address was not at the current PC, advance to current.  */
+  if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now
+      || S_GET_VALUE (frchain_now->frch_cfi_data->last_address)
+	 != frag_now_fix ())
+    cfi_add_advance_loc (symbol_temp_new_now ());
+
+  insn_ptr = alloc_cfi_insn_data ();
+  insn_ptr->insn = CFI_val_encoded_addr;
+  
+  insn_ptr->u.ea.reg = cfi_parse_reg ();
+
+  cfi_parse_separator ();
+  encoding = cfi_parse_const ();
+  if ((encoding & 0xff) != encoding
+      || ((encoding & 0x70) != 0
+#if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
+	  && (encoding & 0x70) != DW_EH_PE_pcrel
+#endif
+	  )
+	 /* leb128 can be handled, but does something actually need it?  */
+      || (encoding & 7) == DW_EH_PE_uleb128
+      || (encoding & 7) > DW_EH_PE_udata8)
+    {
+      as_bad (_("invalid or unsupported encoding in .cfi_lsda"));
+      encoding = DW_EH_PE_omit;
+    }
+
+  cfi_parse_separator ();
+  expression_and_evaluate (&insn_ptr->u.ea.exp);
+  switch (insn_ptr->u.ea.exp.X_op)
+    {
+    case O_symbol:
+      break;
+    case O_constant:
+      if ((encoding & 0x70) != DW_EH_PE_pcrel)
+        break;
+    default:
+      encoding = DW_EH_PE_omit;
+      break;
+    }
+
+  insn_ptr->u.ea.encoding = encoding;
+  if (encoding == DW_EH_PE_omit)
+    {
+      as_bad (_("wrong third argument to .cfi_val_encoded_addr"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+static void
 dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
 {
   int simple = 0;
@@ -1028,6 +1105,64 @@ output_cfi_insn (struct cfi_insn_data *i
 	break;
       }
 
+    case CFI_val_encoded_addr:
+      {
+        unsigned encoding = insn->u.ea.encoding;
+        offsetT encoding_size;
+
+	if (encoding == DW_EH_PE_omit)
+	  break;
+	out_one (DW_CFA_val_expression);
+	out_uleb128 (insn->u.ea.reg);
+
+        switch (encoding & 0x7)
+	  {
+	  case DW_EH_PE_absptr:
+	    encoding_size = DWARF2_ADDR_SIZE (stdoutput);
+	    break;
+	  case DW_EH_PE_udata2:
+	    encoding_size = 2;
+	    break;
+	  case DW_EH_PE_udata4:
+	    encoding_size = 4;
+	    break;
+	  case DW_EH_PE_udata8:
+	    encoding_size = 8;
+	    break;
+	  default:
+	    abort ();
+	  }
+
+	/* If the user has requested absolute encoding,
+	   then use the smaller DW_OP_addr encoding.  */
+	if (insn->u.ea.encoding == DW_EH_PE_absptr)
+	  {
+	    out_uleb128 (1 + encoding_size);
+	    out_one (DW_OP_addr);
+	  }
+	else
+	  {
+	    out_uleb128 (1 + 1 + encoding_size);
+	    out_one (DW_OP_GNU_encoded_addr);
+	    out_one (encoding);
+
+	    if ((encoding & 0x70) == DW_EH_PE_pcrel)
+	      {
+#if CFI_DIFF_EXPR_OK
+		insn->u.ea.exp.X_op = O_subtract;
+		insn->u.ea.exp.X_op_symbol = symbol_temp_new_now ();
+#elif defined (tc_cfi_emit_pcrel_expr)
+		tc_cfi_emit_pcrel_expr (&insn.u.ea.exp, encoding_size);
+		break;
+#else
+		abort ();
+#endif
+	      }
+	  }
+	emit_expr (&insn->u.ea.exp, encoding_size);
+      }
+      break;
+      
     default:
       abort ();
     }
@@ -1292,6 +1427,7 @@ select_cie_for_fde (struct fde_entry *fd
 	      break;
 
 	    case CFI_escape:
+	    case CFI_val_encoded_addr:
 	      /* Don't bother matching these for now.  */
 	      goto fail;
 
@@ -1307,7 +1443,8 @@ select_cie_for_fde (struct fde_entry *fd
 	  && (!j
 	      || j->insn == DW_CFA_advance_loc
 	      || j->insn == DW_CFA_remember_state
-	      || j->insn == CFI_escape))
+	      || j->insn == CFI_escape
+	      || j->insn == CFI_val_encoded_addr))
 	{
 	  *pfirst = j;
 	  return cie;
@@ -1329,7 +1466,8 @@ select_cie_for_fde (struct fde_entry *fd
   for (i = cie->first; i ; i = i->next)
     if (i->insn == DW_CFA_advance_loc
 	|| i->insn == DW_CFA_remember_state
-	|| i->insn == CFI_escape)
+	|| i->insn == CFI_escape
+	|| i->insn == CFI_val_encoded_addr)
       break;
 
   cie->last = i;
Index: gas/doc/as.texinfo
===================================================================
RCS file: /cvs/src/src/gas/doc/as.texinfo,v
retrieving revision 1.183
diff -u -p -r1.183 as.texinfo
--- gas/doc/as.texinfo	13 Aug 2008 02:50:41 -0000	1.183
+++ gas/doc/as.texinfo	24 Sep 2008 22:26:37 -0000
@@ -4230,6 +4230,17 @@ Allows the user to add arbitrary bytes t
 might use this to add OS-specific CFI opcodes, or generic CFI
 opcodes that GAS does not yet support.
 
+@section @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}
+will be encoded in the output file according to @var{encoding}; see the
+description of @code{.cfi_personality} for details on this encoding.
+
+The usefulness of equating a register to a fixed label is probably
+limited to the return address register.  Here, it can be useful to
+mark a code segment that has only one return address which is reached
+by a direct branch and no copy of the return address exists in memory
+or another register.
+
 @node LNS directives
 @section @code{.file @var{fileno} @var{filename}}
 @cindex @code{file} directive
Index: include/elf/dwarf2.h
===================================================================
RCS file: /cvs/src/src/include/elf/dwarf2.h,v
retrieving revision 1.23
diff -u -p -r1.23 dwarf2.h
--- include/elf/dwarf2.h	3 Mar 2008 10:19:01 -0000	1.23
+++ include/elf/dwarf2.h	24 Sep 2008 22:26:50 -0000
@@ -544,6 +544,7 @@ enum dwarf_location_atom
     /* GNU extensions.  */
     DW_OP_GNU_push_tls_address = 0xe0,
     DW_OP_GNU_uninit     = 0xf0,
+    DW_OP_GNU_encoded_addr = 0xf1,
     /* HP extensions.  */
     DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
     DW_OP_HP_is_value    = 0xe1,

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]