Readelf: Handle forward references to CIEs
authorAlan Modra <amodra@gmail.com>
Mon, 22 Sep 2014 08:23:15 +0000 (17:53 +0930)
committerAlan Modra <amodra@gmail.com>
Mon, 22 Sep 2014 09:27:16 +0000 (18:57 +0930)
The linker side of pr16563 was fixed with commit 18cd5bce, but
unfortunately people continue to use older linkers with -flto.  This
means we have binaries with working .eh_frame that can't be dumped by
readelf, and I'm seeing internal IBM bug reports about this fact.

PR 16563
* dwarf.c (GET): Remove semicolon.
(read_cie): New function, extracted from..
(display_debug_frames): ..here.  Correctly handle signed offset
from FDE to CIE in .eh_frame.  Decode forward referenced CIEs too.

binutils/ChangeLog
binutils/dwarf.c

index 223bf11..e8b2daf 100644 (file)
@@ -1,3 +1,11 @@
+2014-09-22  Alan Modra  <amodra@gmail.com>
+
+       PR 16563
+       * dwarf.c (GET): Remove semicolon.
+       (read_cie): New function, extracted from..
+       (display_debug_frames): ..here.  Correctly handle signed offset
+       from FDE to CIE in .eh_frame.  Decode forward referenced CIEs too.
+
 2014-09-16  Nick Clifton  <nickc@redhat.com>
 
        * readelf.c (display_arm_attribute): Use unsigned int type for
index 348e20f..3f4095a 100644 (file)
@@ -5232,10 +5232,97 @@ frame_display_row (Frame_Chunk *fc, int *need_col_headers, int *max_regs)
   printf ("\n");
 }
 
-#define GET(VAR, N)    SAFE_BYTE_GET_AND_INC (VAR, start, N, end);
+#define GET(VAR, N)    SAFE_BYTE_GET_AND_INC (VAR, start, N, end)
 #define LEB()  read_uleb128 (start, & length_return, end); start += length_return
 #define SLEB() read_sleb128 (start, & length_return, end); start += length_return
 
+static unsigned char *
+read_cie (unsigned char *start, unsigned char *end,
+         Frame_Chunk **p_cie, int *p_version,
+         unsigned long *p_aug_len, unsigned char **p_aug)
+{
+  int version;
+  Frame_Chunk *fc;
+  unsigned int length_return;
+  unsigned char *augmentation_data = NULL;
+  unsigned long augmentation_data_len = 0;
+
+  fc = (Frame_Chunk *) xmalloc (sizeof (Frame_Chunk));
+  memset (fc, 0, sizeof (Frame_Chunk));
+
+  fc->col_type = (short int *) xmalloc (sizeof (short int));
+  fc->col_offset = (int *) xmalloc (sizeof (int));
+
+  version = *start++;
+
+  fc->augmentation = (char *) start;
+  start = (unsigned char *) strchr ((char *) start, '\0') + 1;
+
+  if (strcmp (fc->augmentation, "eh") == 0)
+    start += eh_addr_size;
+
+  if (version >= 4)
+    {
+      GET (fc->ptr_size, 1);
+      GET (fc->segment_size, 1);
+      eh_addr_size = fc->ptr_size;
+    }
+  else
+    {
+      fc->ptr_size = eh_addr_size;
+      fc->segment_size = 0;
+    }
+  fc->code_factor = LEB ();
+  fc->data_factor = SLEB ();
+  if (version == 1)
+    {
+      GET (fc->ra, 1);
+    }
+  else
+    {
+      fc->ra = LEB ();
+    }
+
+  if (fc->augmentation[0] == 'z')
+    {
+      augmentation_data_len = LEB ();
+      augmentation_data = start;
+      start += augmentation_data_len;
+    }
+
+  if (augmentation_data_len)
+    {
+      unsigned char *p, *q;
+      p = (unsigned char *) fc->augmentation + 1;
+      q = augmentation_data;
+
+      while (1)
+       {
+         if (*p == 'L')
+           q++;
+         else if (*p == 'P')
+           q += 1 + size_of_encoded_value (*q);
+         else if (*p == 'R')
+           fc->fde_encoding = *q++;
+         else if (*p == 'S')
+           ;
+         else
+           break;
+         p++;
+       }
+    }
+
+  *p_cie = fc;
+  if (p_version)
+    *p_version = version;
+  if (p_aug_len)
+    {
+      *p_aug_len = augmentation_data_len;
+      *p_aug = augmentation_data;
+    }
+  return start;
+}
+
 static int
 display_debug_frames (struct dwarf_section *section,
                      void *file ATTRIBUTE_UNUSED)
@@ -5243,7 +5330,7 @@ display_debug_frames (struct dwarf_section *section,
   unsigned char *start = section->start;
   unsigned char *end = start + section->size;
   unsigned char *section_start = start;
-  Frame_Chunk *chunks = 0;
+  Frame_Chunk *chunks = 0, *forward_refs = 0;
   Frame_Chunk *remembered_state = 0;
   Frame_Chunk *rs;
   int is_eh = strcmp (section->name, ".eh_frame") == 0;
@@ -5306,55 +5393,20 @@ display_debug_frames (struct dwarf_section *section,
                                   || (offset_size == 8 && cie_id == DW64_CIE_ID)))
        {
          int version;
+         int mreg;
 
-         fc = (Frame_Chunk *) xmalloc (sizeof (Frame_Chunk));
-         memset (fc, 0, sizeof (Frame_Chunk));
-
+         start = read_cie (start, end, &cie, &version,
+                           &augmentation_data_len, &augmentation_data);
+         fc = cie;
          fc->next = chunks;
          chunks = fc;
          fc->chunk_start = saved_start;
-         fc->ncols = 0;
-         fc->col_type = (short int *) xmalloc (sizeof (short int));
-         fc->col_offset = (int *) xmalloc (sizeof (int));
-         frame_need_space (fc, max_regs - 1);
-
-         version = *start++;
-
-         fc->augmentation = (char *) start;
-         start = (unsigned char *) strchr ((char *) start, '\0') + 1;
-
-         if (strcmp (fc->augmentation, "eh") == 0)
-           start += eh_addr_size;
-
-         if (version >= 4)
-           {
-             GET (fc->ptr_size, 1);
-             GET (fc->segment_size, 1);
-             eh_addr_size = fc->ptr_size;
-           }
-         else
-           {
-             fc->ptr_size = eh_addr_size;
-             fc->segment_size = 0;
-           }
-         fc->code_factor = LEB ();
-         fc->data_factor = SLEB ();
-         if (version == 1)
-           {
-             GET (fc->ra, 1);
-           }
-         else
-           {
-             fc->ra = LEB ();
-           }
-
-         if (fc->augmentation[0] == 'z')
-           {
-             augmentation_data_len = LEB ();
-             augmentation_data = start;
-             start += augmentation_data_len;
-           }
-         cie = fc;
+         mreg = max_regs - 1;
+         if (mreg < fc->ra)
+           mreg = fc->ra;
+         frame_need_space (fc, mreg);
+         if (fc->fde_encoding)
+           encoded_ptr_size = size_of_encoded_value (fc->fde_encoding);
 
          printf ("\n%08lx ", (unsigned long) (saved_start - section_start));
          print_dwarf_vma (length, fc->ptr_size);
@@ -5389,33 +5441,6 @@ display_debug_frames (struct dwarf_section *section,
                }
              putchar ('\n');
            }
-
-         if (augmentation_data_len)
-           {
-             unsigned char *p, *q;
-             p = (unsigned char *) fc->augmentation + 1;
-             q = augmentation_data;
-
-             while (1)
-               {
-                 if (*p == 'L')
-                   q++;
-                 else if (*p == 'P')
-                   q += 1 + size_of_encoded_value (*q);
-                 else if (*p == 'R')
-                   fc->fde_encoding = *q++;
-                 else if (*p == 'S')
-                   ;
-                 else
-                   break;
-                 p++;
-               }
-
-             if (fc->fde_encoding)
-               encoded_ptr_size = size_of_encoded_value (fc->fde_encoding);
-           }
-
-         frame_need_space (fc, fc->ra);
        }
       else
        {
@@ -5423,14 +5448,70 @@ display_debug_frames (struct dwarf_section *section,
          static Frame_Chunk fde_fc;
          unsigned long segment_selector;
 
-         fc = & fde_fc;
-         memset (fc, 0, sizeof (Frame_Chunk));
+         if (is_eh)
+           {
+             dwarf_vma sign = (dwarf_vma) 1 << (offset_size * 8 - 1);
+             look_for = start - 4 - ((cie_id ^ sign) - sign);
+           }
+         else
+           look_for = section_start + cie_id;
+
+         if (look_for <= saved_start)
+           {
+             for (cie = chunks; cie ; cie = cie->next)
+               if (cie->chunk_start == look_for)
+                 break;
+           }
+         else
+           {
+             for (cie = forward_refs; cie ; cie = cie->next)
+               if (cie->chunk_start == look_for)
+                 break;
+             if (!cie)
+               {
+                 unsigned int off_size;
+                 unsigned char *cie_scan;
 
-         look_for = is_eh ? start - 4 - cie_id : section_start + cie_id;
+                 cie_scan = look_for;
+                 off_size = 4;
+                 SAFE_BYTE_GET_AND_INC (length, cie_scan, 4, end);
+                 if (length == 0xffffffff)
+                   {
+                     SAFE_BYTE_GET_AND_INC (length, cie_scan, 8, end);
+                     off_size = 8;
+                   }
+                 if (length != 0)
+                   {
+                     dwarf_vma c_id;
 
-         for (cie = chunks; cie ; cie = cie->next)
-           if (cie->chunk_start == look_for)
-             break;
+                     SAFE_BYTE_GET_AND_INC (c_id, cie_scan, off_size, end);
+                     if (is_eh
+                         ? c_id == 0
+                         : ((off_size == 4 && c_id == DW_CIE_ID)
+                            || (off_size == 8 && c_id == DW64_CIE_ID)))
+                       {
+                         int version;
+                         int mreg;
+
+                         read_cie (cie_scan, end, &cie, &version,
+                                   &augmentation_data_len, &augmentation_data);
+                         cie->next = forward_refs;
+                         forward_refs = cie;
+                         cie->chunk_start = look_for;
+                         mreg = max_regs - 1;
+                         if (mreg < cie->ra)
+                           mreg = cie->ra;
+                         frame_need_space (cie, mreg);
+                         if (cie->fde_encoding)
+                           encoded_ptr_size
+                             = size_of_encoded_value (cie->fde_encoding);
+                       }
+                   }
+               }
+           }
+
+         fc = &fde_fc;
+         memset (fc, 0, sizeof (Frame_Chunk));
 
          if (!cie)
            {