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]

[PATCH] Fix DW_CFA_set_loc handling


Hi!

Recent http://gcc.gnu.org/PR22313 change (including gcc 4.1 branch) started
(unnecessarily) using DW_CFA_set_loc at the start of all FDEs, but
unfortunately linker .eh_frame optimizations didn't handle that well.
If FDE encoding is pc relative or linker is changing it from absptr
to pc relative, we need to adjust the DW_CFA_set_loc operand accordingly.

Tested with make check on x86_64-linux, visual inspection of x86_64
readelf -wf libc.so.6 (this was seriously broken before) and visual
inspection of a hacked up test for absptr -> pcrel conversion.

Can anyone test this on e.g. mips (which I believe heavily converts from
absolute to relative)?

2006-09-26  Jakub Jelinek  <jakub@redhat.com>

	* elf-bfd.h (struct eh_cie_fde): Add set_loc pointer.
	* elf-eh-frame.c (skip_cfa_op): Fix handling of DW_CFA_advance_loc.
	Handle DW_CFA_{remember,restore}_state, DW_CFA_GNU_window_save,
	DW_CFA_val_{offset{,_sf},expression}.
	(skip_non_nops): Record number of DW_CFA_set_loc ops.
	(_bfd_elf_discard_section_eh_frame): Require skip_non_nops recognizes
	all ops.  If there are any DW_CFA_set_loc ops and they are pcrel
	or going to be pcrel, compute set_loc array.
	(_bfd_elf_eh_frame_section_offset): If make_relative, kill relocations
	against DW_CFA_set_loc operands.
	(_bfd_elf_write_section_eh_frame): Handle DW_CFA_set_loc adjusting.

--- bfd/elf-eh-frame.c.jj	2006-06-29 14:25:14.000000000 +0200
+++ bfd/elf-eh-frame.c	2006-09-26 14:12:57.000000000 +0200
@@ -273,11 +273,14 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
   if (!read_byte (iter, end, &op))
     return FALSE;
 
-  switch (op & 0x80 ? op & 0xc0 : op)
+  switch (op & 0xc0 ? op & 0xc0 : op)
     {
     case DW_CFA_nop:
     case DW_CFA_advance_loc:
     case DW_CFA_restore:
+    case DW_CFA_remember_state:
+    case DW_CFA_restore_state:
+    case DW_CFA_GNU_window_save:
       /* No arguments.  */
       return TRUE;
 
@@ -292,6 +295,8 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
       /* One leb128 argument.  */
       return skip_leb128 (iter, end);
 
+    case DW_CFA_val_offset:
+    case DW_CFA_val_offset_sf:
     case DW_CFA_offset_extended:
     case DW_CFA_register:
     case DW_CFA_def_cfa:
@@ -308,6 +313,7 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
 	      && skip_bytes (iter, end, length));
 
     case DW_CFA_expression:
+    case DW_CFA_val_expression:
       /* A leb128 followed by a variable-length argument.  */
       return (skip_leb128 (iter, end)
 	      && read_uleb128 (iter, end, &length)
@@ -339,7 +345,8 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
    ENCODED_PTR_WIDTH is as for skip_cfa_op.  */
 
 static bfd_byte *
-skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width)
+skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width,
+	       unsigned int *set_loc_count)
 {
   bfd_byte *last;
 
@@ -349,6 +356,8 @@ skip_non_nops (bfd_byte *buf, bfd_byte *
       buf++;
     else
       {
+	if (*buf == DW_CFA_set_loc)
+	  ++*set_loc_count;
 	if (!skip_cfa_op (&buf, end, encoded_ptr_width))
 	  return 0;
 	last = buf;
@@ -453,8 +462,9 @@ _bfd_elf_discard_section_eh_frame
   for (;;)
     {
       char *aug;
-      bfd_byte *start, *end, *insns;
+      bfd_byte *start, *end, *insns, *insns_end;
       bfd_size_type length;
+      unsigned int set_loc_count;
 
       if (sec_info->count == sec_info->alloced)
 	{
@@ -558,6 +568,7 @@ _bfd_elf_discard_section_eh_frame
 	  cie_usage_count = 0;
 	  memset (&cie, 0, sizeof (cie));
 	  cie.hdr = hdr;
+	  start = buf;
 	  REQUIRE (read_byte (&buf, end, &cie.version));
 
 	  /* Cannot handle unknown versions.  */
@@ -775,11 +786,38 @@ _bfd_elf_discard_section_eh_frame
 
       /* Try to interpret the CFA instructions and find the first
 	 padding nop.  Shrink this_inf's size so that it doesn't
-	 including the padding.  */
+	 include the padding.  */
       length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
-      insns = skip_non_nops (insns, end, length);
-      if (insns != 0)
-	this_inf->size -= end - insns;
+      set_loc_count = 0;
+      insns_end = skip_non_nops (insns, end, length, &set_loc_count);
+      /* If we don't understand the CFA instructions, we can't know
+	 what needs to be adjusted there.  */
+      if (insns_end == NULL
+	  /* For the time being we don't support DW_CFA_set_loc in
+	     CIE instructions.  */
+	  || (set_loc_count && this_inf->cie))
+	goto free_no_table;
+      this_inf->size -= end - insns_end;
+      if (set_loc_count
+	  && ((cie.fde_encoding & 0xf0) == DW_EH_PE_pcrel
+	      || cie.make_relative))
+	{
+	  unsigned int cnt;
+	  bfd_byte *p;
+
+	  this_inf->set_loc = bfd_malloc ((set_loc_count + 1)
+					  * sizeof (unsigned int));
+	  REQUIRE (this_inf->set_loc);
+	  this_inf->set_loc[0] = set_loc_count;
+	  p = insns;
+	  cnt = 0;
+	  while (p < end)
+	    {
+	      if (*p == DW_CFA_set_loc)
+		this_inf->set_loc[++cnt] = p + 1 - start;
+	      REQUIRE (skip_cfa_op (&p, end, length));
+	    }
+	}
 
       this_inf->fde_encoding = cie.fde_encoding;
       this_inf->lsda_encoding = cie.lsda_encoding;
@@ -965,6 +1003,23 @@ _bfd_elf_eh_frame_section_offset (bfd *o
       return (bfd_vma) -2;
     }
 
+  /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
+     relocation against DW_CFA_set_loc's arguments.  */
+  if (sec_info->entry[mid].set_loc
+      && (sec_info->entry[mid].cie
+	  ? sec_info->entry[mid].make_relative
+	  : sec_info->entry[mid].cie_inf->make_relative)
+      && (offset >= sec_info->entry[mid].offset + 8
+		    + sec_info->entry[mid].set_loc[1]))
+    {
+      unsigned int cnt;
+
+      for (cnt = 1; cnt <= sec_info->entry[mid].set_loc[0]; cnt++)
+	if (offset == sec_info->entry[mid].offset + 8
+		      + sec_info->entry[mid].set_loc[cnt])
+	  return (bfd_vma) -2;
+    }
+
   if (hdr_info->offsets_adjusted)
     offset -= sec->output_offset;
   /* Any new augmentation bytes go before the first relocation.  */
@@ -1189,6 +1244,7 @@ _bfd_elf_write_section_eh_frame (bfd *ab
 	  /* FDE */
 	  bfd_vma value, address;
 	  unsigned int width;
+	  bfd_byte *start;
 
 	  /* Skip length.  */
 	  buf += 4;
@@ -1225,6 +1281,8 @@ _bfd_elf_write_section_eh_frame (bfd *ab
 	      write_value (abfd, buf, value, width);
 	    }
 
+	  start = buf;
+
 	  if (hdr_info)
 	    {
 	      hdr_info->array[hdr_info->array_count].initial_loc = address;
@@ -1257,6 +1315,36 @@ _bfd_elf_write_section_eh_frame (bfd *ab
 	      memmove (buf + 1, buf, end - buf);
 	      *buf = 0;
 	    }
+
+	  if (ent->set_loc)
+	    {
+	      /* Adjust DW_CFA_set_loc.  */
+	      unsigned int cnt, width;
+	      bfd_vma new_offset;
+
+	      width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+	      new_offset = ent->new_offset + 8
+			   + extra_augmentation_string_bytes (ent)
+			   + extra_augmentation_data_bytes (ent);
+
+	      for (cnt = 1; cnt <= ent->set_loc[0]; cnt++)
+		{
+		  bfd_vma value;
+		  buf = start + ent->set_loc[cnt];
+
+		  value = read_value (abfd, buf, width,
+				      get_DW_EH_PE_signed (ent->fde_encoding));
+		  if (!value)
+		    continue;
+
+		  if ((ent->fde_encoding & 0xf0) == DW_EH_PE_pcrel)
+		    value += ent->offset + 8 - new_offset;
+		  if (ent->cie_inf->make_relative)
+		    value -= sec->output_section->vma + new_offset
+			     + ent->set_loc[cnt];
+		  write_value (abfd, buf, value, width);
+		}
+	    }
 	}
     }
 
--- bfd/elf-bfd.h.jj	2006-09-26 07:42:56.000000000 +0200
+++ bfd/elf-bfd.h	2006-09-26 13:38:45.000000000 +0200
@@ -304,6 +304,7 @@ struct eh_cie_fde
   unsigned int make_lsda_relative : 1;
   unsigned int need_lsda_relative : 1;
   unsigned int per_encoding_relative : 1;
+  unsigned int *set_loc;
 };
 
 struct eh_frame_sec_info


	Jakub


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