2002-06-21 Michal Ludvig <mludvig@suse.cz>
authorMichal Ludvig <mludvig@suse.cz>
Fri, 21 Jun 2002 12:12:34 +0000 (12:12 +0000)
committerMichal Ludvig <mludvig@suse.cz>
Fri, 21 Jun 2002 12:12:34 +0000 (12:12 +0000)
* dwarf2cfi.c (read_encoded_pointer): Don't handle pointer
encoding anymore.
(pointer_encoding, enum ptr_encoding): New.
(execute_cfa_program): Take care about pointer encoding.
(dwarf2_build_frame_info): Only call parse_frame_info for
.debug_frame and .eh_frame.
(parse_frame_info): New, derived from former dwarf2_build_frame_info.
fixed augmentation handling, added relative addressing,
ignore duplicate FDEs. Added comments.

gdb/ChangeLog
gdb/dwarf2cfi.c

index f69031bfaae7fe973f96f557e94c98ead5038eec..76c3a8cd5177b980fc292f0f0bc22e4bc565d1dc 100644 (file)
@@ -1,3 +1,15 @@
+2002-06-21  Michal Ludvig  <mludvig@suse.cz>
+
+       * dwarf2cfi.c (read_encoded_pointer): Don't handle pointer 
+       encoding anymore.
+       (pointer_encoding, enum ptr_encoding): New.
+       (execute_cfa_program): Take care about pointer encoding.        
+       (dwarf2_build_frame_info): Only call parse_frame_info for 
+       .debug_frame and .eh_frame.
+       (parse_frame_info): New, derived from former dwarf2_build_frame_info.
+       fixed augmentation handling, added relative addressing, 
+       ignore duplicate FDEs. Added comments.
+       
 2002-06-20  Elena Zannoni  <ezannoni@redhat.com>
 
        * event-top.c (command_handler): Don't use space_at_cmd_start
index ea501551c8e6412ef0402de9d626ea6aca924a7b..ed319fb64b642df81c46d47e33ce3f90b42bd91c 100644 (file)
@@ -34,7 +34,7 @@
    Frame Descriptors.  */
 struct cie_unit
 {
-  /* Offset of this unit in dwarf_frame_buffer.  */
+  /* Offset of this unit in .debug_frame or .eh_frame.  */
   ULONGEST offset;
 
   /* A null-terminated string that identifies the augmentation to this CIE or
@@ -176,6 +176,14 @@ struct frame_state
   struct objfile *objfile;
 };
 
+enum ptr_encoding { 
+  PE_absptr = DW_EH_PE_absptr,
+  PE_pcrel = DW_EH_PE_pcrel,
+  PE_textrel = DW_EH_PE_textrel,
+  PE_datarel = DW_EH_PE_datarel,
+  PE_funcrel = DW_EH_PE_funcrel
+};
+
 #define UNWIND_CONTEXT(fi) ((struct context *) (fi->context))
 \f
 
@@ -188,8 +196,6 @@ extern file_ptr dwarf_frame_offset;
 extern unsigned int dwarf_frame_size;
 extern file_ptr dwarf_eh_frame_offset;
 extern unsigned int dwarf_eh_frame_size;
-
-static char *dwarf_frame_buffer;
 \f
 
 extern char *dwarf2_read_section (struct objfile *objfile, file_ptr offset,
@@ -219,6 +225,7 @@ static LONGEST read_sleb128 (bfd * abfd, char **p);
 static CORE_ADDR read_pointer (bfd * abfd, char **p);
 static CORE_ADDR read_encoded_pointer (bfd * abfd, char **p,
                                       unsigned char encoding);
+static enum ptr_encoding pointer_encoding (unsigned char encoding);
 
 static LONGEST read_initial_length (bfd * abfd, char *buf, int *bytes_read);
 static ULONGEST read_length (bfd * abfd, char *buf, int *bytes_read,
@@ -494,6 +501,9 @@ read_pointer (bfd * abfd, char **p)
     }
 }
 
+/* This functions only reads appropriate amount of data from *p 
+ * and returns the resulting value. Calling function must handle
+ * different encoding possibilities itself!  */
 static CORE_ADDR
 read_encoded_pointer (bfd * abfd, char **p, unsigned char encoding)
 {
@@ -537,22 +547,34 @@ read_encoded_pointer (bfd * abfd, char **p, unsigned char encoding)
                      "read_encoded_pointer: unknown pointer encoding");
     }
 
-  if (ret != 0)
-    switch (encoding & 0xf0)
-      {
-      case DW_EH_PE_absptr:
-       break;
-      case DW_EH_PE_pcrel:
-       ret += (CORE_ADDR) * p;
-       break;
-      case DW_EH_PE_textrel:
-      case DW_EH_PE_datarel:
-      case DW_EH_PE_funcrel:
-      default:
-       internal_error (__FILE__, __LINE__,
-                       "read_encoded_pointer: unknown pointer encoding");
-      }
+  return ret;
+}
+
+/* Variable 'encoding' carries 3 different flags:
+ * - encoding & 0x0f : size of the address (handled in read_encoded_pointer())
+ * - encoding & 0x70 : type (absolute, relative, ...)
+ * - encoding & 0x80 : indirect flag (DW_EH_PE_indirect == 0x80).  */
+enum ptr_encoding
+pointer_encoding (unsigned char encoding)
+{
+  int ret;
 
+  if (encoding & DW_EH_PE_indirect)
+    warning ("CFI: Unsupported pointer encoding: DW_EH_PE_indirect");
+  
+  switch (encoding & 0x70)
+    {
+    case DW_EH_PE_absptr:
+    case DW_EH_PE_pcrel:
+    case DW_EH_PE_textrel:
+    case DW_EH_PE_datarel:
+    case DW_EH_PE_funcrel:
+      ret = encoding & 0x70;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+                 "CFI: unknown pointer encoding");
+    }
   return ret;
 }
 
@@ -627,6 +649,10 @@ execute_cfa_program (struct objfile *objfile, char *insn_ptr, char *insn_end,
          case DW_CFA_set_loc:
            fs->pc = read_encoded_pointer (objfile->obfd, &insn_ptr,
                                           fs->addr_encoding);
+           
+           if (pointer_encoding (fs->addr_encoding) != PE_absptr)
+               warning ("CFI: DW_CFA_set_loc uses relative addressing");
+           
            break;
 
          case DW_CFA_advance_loc1:
@@ -1380,39 +1406,46 @@ compare_fde_unit (const void *a, const void *b)
 }
 
 /*  Build the cie_chunks and fde_chunks tables from informations
-    in .debug_frame section.  */
-void
-dwarf2_build_frame_info (struct objfile *objfile)
+    found in .debug_frame and .eh_frame sections.  */
+/* We can handle both of these sections almost in the same way, however there
+   are some exceptions:
+   - CIE ID is -1 in debug_frame, but 0 in eh_frame
+   - eh_frame may contain some more information that are used only by gcc 
+     (eg. personality pointer, LSDA pointer, ...). Most of them we can ignore.
+   - In debug_frame FDE's item cie_id contains offset of it's parent CIE.
+     In eh_frame FDE's item cie_id is a relative pointer to the parent CIE.
+     Anyway we don't need to bother with this, because we are smart enough 
+     to keep the pointer to the parent CIE of oncomming FDEs in 'last_cie'.
+   - Although debug_frame items can contain Augmentation as well as 
+     eh_frame ones, I have never seen them non-empty. Thus only in eh_frame 
+     we can encounter for example non-absolute pointers (Aug. 'R').  
+                                                              -- mludvig  */
+static void
+parse_frame_info (struct objfile *objfile, file_ptr frame_offset,
+                 unsigned int frame_size, int eh_frame)
 {
   bfd *abfd = objfile->obfd;
+  asection *curr_section_ptr;
   char *start = NULL;
   char *end = NULL;
-  int from_eh = 0;
+  char *frame_buffer = NULL;
+  char *curr_section_name, *aug_data;
+  struct cie_unit *last_cie = NULL;
+  int last_dup_fde = 0;
+  int aug_len, i;
+  CORE_ADDR curr_section_vma = 0;
 
   unwind_tmp_obstack_init ();
 
-  dwarf_frame_buffer = 0;
+  frame_buffer = dwarf2_read_section (objfile, frame_offset, frame_size);
 
-  if (dwarf_frame_offset)
-    {
-      dwarf_frame_buffer = dwarf2_read_section (objfile,
-                                               dwarf_frame_offset,
-                                               dwarf_frame_size);
+  start = frame_buffer;
+  end = frame_buffer + frame_size;
 
-      start = dwarf_frame_buffer;
-      end = dwarf_frame_buffer + dwarf_frame_size;
-    }
-  else if (dwarf_eh_frame_offset)
-    {
-      dwarf_frame_buffer = dwarf2_read_section (objfile,
-                                               dwarf_eh_frame_offset,
-                                               dwarf_eh_frame_size);
-
-      start = dwarf_frame_buffer;
-      end = dwarf_frame_buffer + dwarf_eh_frame_size;
-
-      from_eh = 1;
-    }
+  curr_section_name = eh_frame ? ".eh_frame" : ".debug_frame";
+  curr_section_ptr = bfd_get_section_by_name (abfd, curr_section_name);
+  if (curr_section_ptr)
+    curr_section_vma = curr_section_ptr->vma;
 
   if (start)
     {
@@ -1420,9 +1453,8 @@ dwarf2_build_frame_info (struct objfile *objfile)
        {
          unsigned long length;
          ULONGEST cie_id;
-         ULONGEST unit_offset = start - dwarf_frame_buffer;
-         int bytes_read;
-         int dwarf64;
+         ULONGEST unit_offset = start - frame_buffer;
+         int bytes_read, dwarf64, flag_pcrel;
          char *block_end;
 
          length = read_initial_length (abfd, start, &bytes_read);
@@ -1430,10 +1462,16 @@ dwarf2_build_frame_info (struct objfile *objfile)
          dwarf64 = (bytes_read == 12);
          block_end = start + length;
 
+         if (length == 0)
+           {
+             start = block_end;
+             continue;
+           }
+
          cie_id = read_length (abfd, start, &bytes_read, dwarf64);
          start += bytes_read;
 
-         if ((from_eh && cie_id == 0) || is_cie (cie_id, dwarf64))
+         if ((eh_frame && cie_id == 0) || is_cie (cie_id, dwarf64))
            {
              struct cie_unit *cie = cie_unit_alloc ();
              char *aug;
@@ -1449,87 +1487,186 @@ dwarf2_build_frame_info (struct objfile *objfile)
              start++;          /* version */
 
              cie->augmentation = aug = start;
-             while (*start)
-               start++;
-             start++;          /* skip past NUL */
+             while (*start++); /* Skips last NULL as well */
 
              cie->code_align = read_uleb128 (abfd, &start);
              cie->data_align = read_sleb128 (abfd, &start);
              cie->ra = read_1u (abfd, &start);
 
+             /* Augmentation:
+                z      Indicates that a uleb128 is present to size the
+                augmentation section.
+                L      Indicates the encoding (and thus presence) of
+                an LSDA pointer in the FDE augmentation.
+                R      Indicates a non-default pointer encoding for
+                FDE code pointers.
+                P      Indicates the presence of an encoding + language
+                personality routine in the CIE augmentation.
+
+                [This info comes from GCC's dwarf2out.c]
+              */
              if (*aug == 'z')
                {
-                 int xtra = read_uleb128 (abfd, &start);
-                 start += xtra;
+                 aug_len = read_uleb128 (abfd, &start);
+                 aug_data = start;
+                 start += aug_len;
                  ++aug;
                }
 
+             cie->data = start;
+             cie->data_length = block_end - cie->data;
+
              while (*aug != '\0')
                {
                  if (aug[0] == 'e' && aug[1] == 'h')
                    {
-                     start += sizeof (void *);
-                     aug += 2;
+                     aug_data += sizeof (void *);
+                     aug++;
                    }
                  else if (aug[0] == 'R')
+                   cie->addr_encoding = *aug_data++;
+                 else if (aug[0] == 'P')
                    {
-                     cie->addr_encoding = *start++;
-                     aug += 1;
+                     CORE_ADDR pers_addr;
+                     int pers_addr_enc;
+
+                     pers_addr_enc = *aug_data++;
+                     /* We don't need pers_addr value and so we 
+                        don't care about it's encoding.  */
+                     pers_addr = read_encoded_pointer (abfd, &aug_data,
+                                                       pers_addr_enc);
                    }
-                 else if (aug[0] == 'P')
+                 else if (aug[0] == 'L' && eh_frame)
                    {
-                     CORE_ADDR ptr;
-                     ptr = read_encoded_pointer (abfd, &start,
-                                                 cie->addr_encoding);
-                     aug += 1;
+                     int lsda_addr_enc;
+
+                     /* Perhaps we should save this to CIE for later use?
+                        Do we need it for something in GDB?  */
+                     lsda_addr_enc = *aug_data++;
                    }
                  else
-                   warning ("%s(): unknown augmentation", __func__);
+                   warning ("CFI warning: unknown augmentation \"%c\""
+                            " in \"%s\" of\n"
+                            "\t%s", aug[0], curr_section_name,
+                            objfile->name);
+                 aug++;
                }
 
-             cie->data = start;
-             cie->data_length = block_end - start;
+             last_cie = cie;
            }
          else
            {
              struct fde_unit *fde;
              struct cie_unit *cie;
+             int dup = 0;
+             CORE_ADDR init_loc;
+
+             /* We assume that debug_frame is in order 
+                CIE,FDE,CIE,FDE,FDE,...  and thus the CIE for this FDE
+                should be stored in last_cie pointer. If not, we'll 
+                try to find it by the older way.  */
+             if (last_cie)
+               cie = last_cie;
+             else
+               {
+                 warning ("CFI: last_cie == NULL. "
+                          "Perhaps a malformed %s section in '%s'...?\n",
+                          curr_section_name, objfile->name);
 
-             fde_chunks_need_space ();
-             fde = fde_unit_alloc ();
+                 cie = cie_chunks;
+                 while (cie)
+                   {
+                     if (cie->objfile == objfile)
+                       {
+                         if (eh_frame &&
+                             (cie->offset ==
+                              (unit_offset + bytes_read - cie_id)))
+                           break;
+                         if (!eh_frame && (cie->offset == cie_id))
+                           break;
+                       }
+
+                     cie = cie->next;
+                   }
+                 if (!cie)
+                   error ("CFI: can't find CIE pointer");
+               }
 
-             fde_chunks.array[fde_chunks.elems++] = fde;
+             init_loc = read_encoded_pointer (abfd, &start,
+                                              cie->addr_encoding);
 
-             fde->initial_location = read_pointer (abfd, &start)
-               + ANOFFSET (objfile->section_offsets,
-                           SECT_OFF_TEXT (objfile));
-             fde->address_range = read_pointer (abfd, &start);
+             switch (pointer_encoding (cie->addr_encoding))
+             {
+               case PE_absptr:
+                       break;
+               case PE_pcrel:
+                       /* start-frame_buffer gives offset from 
+                          the beginning of actual section.  */
+                       init_loc += curr_section_vma + start - frame_buffer;
+                       break;
+               default:
+                       warning ("CFI: Unsupported pointer encoding\n");
+             }
 
-             cie = cie_chunks;
-             while (cie)
+             /* For relocatable objects we must add an offset telling
+                where the section is actually mapped in the memory.  */
+             init_loc += ANOFFSET (objfile->section_offsets,
+                                   SECT_OFF_TEXT (objfile));
+
+             /* If we have both .debug_frame and .eh_frame present in 
+                a file, we must eliminate duplicate FDEs. For now we'll 
+                run through all entries in fde_chunks and check it one 
+                by one. Perhaps in the future we can implement a faster 
+                searching algorithm.  */
+             /* eh_frame==2 indicates, that this file has an already 
+                parsed .debug_frame too. When eh_frame==1 it means, that no
+                .debug_frame is present and thus we don't need to check for
+                duplicities. eh_frame==0 means, that we parse .debug_frame
+                and don't need to care about duplicate FDEs, because
+                .debug_frame is parsed first.  */
+             if (eh_frame == 2)
+               for (i = 0; eh_frame == 2 && i < fde_chunks.elems; i++)
                {
-                 if (cie->objfile == objfile)
+                 /* We assume that FDEs in .debug_frame and .eh_frame 
+                    have the same order (if they are present, of course).
+                    If we find a duplicate entry for one FDE and save
+                    it's index to last_dup_fde it's very likely, that 
+                    we'll find an entry for the following FDE right after 
+                    the previous one. Thus in many cases we'll run this 
+                    loop only once.  */
+                 last_dup_fde = (last_dup_fde + i) % fde_chunks.elems;
+                 if (fde_chunks.array[last_dup_fde]->initial_location
+                     == init_loc)
                    {
-                     if (from_eh
-                         && (cie->offset ==
-                             (unit_offset + bytes_read - cie_id)))
-                       break;
-                     if (!from_eh && (cie->offset == cie_id))
-                       break;
+                     dup = 1;
+                     break;
                    }
-
-                 cie = cie->next;
                }
 
-             if (!cie)
-               error ("%s(): can't find CIE pointer", __func__);
-             fde->cie_ptr = cie;
+             /* Allocate a new entry only if this FDE isn't a duplicate of
+                something we have already seen.   */
+             if (!dup)
+               {
+                 fde_chunks_need_space ();
+                 fde = fde_unit_alloc ();
+
+                 fde_chunks.array[fde_chunks.elems++] = fde;
+
+                 fde->initial_location = init_loc;
+                 fde->address_range = read_encoded_pointer (abfd, &start,
+                                                            cie->
+                                                            addr_encoding);
 
-             if (cie->augmentation[0] == 'z')
-               read_uleb128 (abfd, &start);
+                 fde->cie_ptr = cie;
 
-             fde->data = start;
-             fde->data_length = block_end - start;
+                 /* Here we intentionally ignore augmentation data
+                    from FDE, because we don't need them.  */
+                 if (cie->augmentation[0] == 'z')
+                   start += read_uleb128 (abfd, &start);
+
+                 fde->data = start;
+                 fde->data_length = block_end - start;
+               }
            }
          start = block_end;
        }
@@ -1537,7 +1674,30 @@ dwarf2_build_frame_info (struct objfile *objfile)
             sizeof (struct fde_unit *), compare_fde_unit);
     }
 }
-\f
+
+/* We must parse both .debug_frame section and .eh_frame because 
+ * not all frames must be present in both of these sections. */
+void
+dwarf2_build_frame_info (struct objfile *objfile)
+{
+  int after_debug_frame = 0;
+
+  /* If we have .debug_frame then the parser is called with 
+     eh_frame==0 for .debug_frame and eh_frame==2 for .eh_frame, 
+     otherwise it's only called once for .eh_frame with argument 
+     eh_frame==1.  */
+
+  if (dwarf_frame_offset)
+  {
+    parse_frame_info (objfile, dwarf_frame_offset,
+                     dwarf_frame_size, 0 /* = debug_frame */);
+    after_debug_frame = 1;
+  }
+
+  if (dwarf_eh_frame_offset)
+    parse_frame_info (objfile, dwarf_eh_frame_offset, dwarf_eh_frame_size, 
+                     1 /* = eh_frame */ + after_debug_frame);
+}
 
 /* Return the frame address.  */
 CORE_ADDR