]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Add dwarf_cfi_frames iterator.
authorRoland McGrath <roland@redhat.com>
Wed, 23 Jun 2010 09:44:15 +0000 (02:44 -0700)
committerRoland McGrath <roland@redhat.com>
Wed, 23 Jun 2010 09:44:15 +0000 (02:44 -0700)
libdw/ChangeLog
libdw/cfi.c
libdw/cfi.h
libdw/dwarf_getcfi.c
libdw/dwarf_getcfi_elf.c
libdw/fde.c
libdw/libdw.h

index 17658581db131c45258b3fd868dfbe9e8c8324b3..c22540aa8785f3a4ccf285d8dfbaedf009454433 100644 (file)
@@ -1,3 +1,27 @@
+2010-06-23  Roland McGrath  <roland@redhat.com>
+
+       * cfi.c (dwarf_cfi_frames): New function.
+       * libdw.h: Declare it.
+
+       * fde.c (__libdw_fde_by_offset): Take new argument NEXTOFF.
+       If non-null, return -1 marker without error at end of section,
+       skip CIEs, and store next offset.  Maintain fde_tailp and next.
+       * cfi.h: Update decl.
+
+       * cfi.h (struct dwarf_fde): New member next.
+       (struct Dwarf_CFI_s): New members first_fde, fde_tailp.
+       * dwarf_getcfi.c (dwarf_getcfi): Initialize them.
+       * dwarf_getcfi_elf.c (allocate_cfi): Likewise.
+       * fde.c (intern_fde): Initialize next.
+
+       * cfi.c (clear_prev): New function, broken out of ...
+       (execute_cfi): ... here.  Call it.  Take new flag argument; when
+       false, stop at the first PC change and don't clear_prev.
+       (cie_cache_initial_state, __libdw_frame_at_address): Update callers.
+
+       * cfi.h (struct Dwarf_Frame_s): New member fde_pc.
+       * cfi.c (execute_cfi): Set it to pending PROGRAM pointer at return.
+
 2010-06-22  Roland McGrath  <roland@redhat.com>
 
        * dwarf_getlocation.c (check_constant_offset): data[48] are constant.
index e49335ddc54da2c73c01548f333a00dad8dfb876..2ea7cfdd9829a5526a9ecd361201c512810157ac 100644 (file)
@@ -76,12 +76,23 @@ duplicate_frame_state (const Dwarf_Frame *original,
   return copy;
 }
 
+static void
+clear_prev (Dwarf_Frame *fs)
+{
+  while (fs->prev != NULL)
+    {
+      Dwarf_Frame *prev = fs->prev;
+      fs->prev = prev->prev;
+      free (prev);
+    }
+}
+
 /* Returns a DWARF_E_* error code, usually NOERROR or INVALID_CFI.
    Frees *STATE on failure.  */
 static int
 execute_cfi (Dwarf_CFI *cache,
             const struct dwarf_cie *cie,
-            Dwarf_Frame **state,
+            Dwarf_Frame **state, bool searching,
             const uint8_t *program, const uint8_t *const end, bool abi_cfi,
             Dwarf_Addr loc, Dwarf_Addr find_pc)
 {
@@ -370,7 +381,7 @@ execute_cfi (Dwarf_CFI *cache,
 
       /* We get here only for the cases that have just moved LOC.  */
       cfi_assert (cie->initial_state != NULL);
-      if (find_pc >= loc)
+      if (searching && find_pc >= loc)
        /* This advance has not yet reached FIND_PC.  */
        fs->start = loc;
       else
@@ -378,6 +389,7 @@ execute_cfi (Dwarf_CFI *cache,
          /* We have just advanced past the address we're looking for.
             The state currently described is what we want to see.  */
          fs->end = loc;
+         fs->fde_pc = program;
          break;
        }
     }
@@ -396,13 +408,9 @@ execute_cfi (Dwarf_CFI *cache,
 
  out:
 
-  /* Pop any remembered states left on the stack.  */
-  while (fs->prev != NULL)
-    {
-      Dwarf_Frame *prev = fs->prev;
-      fs->prev = prev->prev;
-      free (prev);
-    }
+  if (searching || unlikely (result != DWARF_E_NOERROR))
+    /* Pop any remembered states left on the stack.  */
+    clear_prev (fs);
 
   if (likely (result == DWARF_E_NOERROR))
     *state = fs;
@@ -455,7 +463,7 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct dwarf_cie *cie)
          .code_alignment_factor = abi_info.code_alignment_factor,
          .data_alignment_factor = abi_info.data_alignment_factor,
        };
-      result = execute_cfi (cache, &abi_cie, &cie_fs,
+      result = execute_cfi (cache, &abi_cie, &cie_fs, true,
                            abi_info.initial_instructions,
                            abi_info.initial_instructions_end, true,
                            0, (Dwarf_Addr) -1l);
@@ -464,7 +472,7 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct dwarf_cie *cie)
   /* Now run the CIE's initial instructions.  */
   if (cie->initial_instructions_end > cie->initial_instructions
       && likely (result == DWARF_E_NOERROR))
-    result = execute_cfi (cache, cie, &cie_fs,
+    result = execute_cfi (cache, cie, &cie_fs, true,
                          cie->initial_instructions,
                          cie->initial_instructions_end, false,
                          0, (Dwarf_Addr) -1l);
@@ -496,7 +504,7 @@ __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde,
       fs->start = fde->start;
       fs->end = fde->end;
 
-      result = execute_cfi (cache, fde->cie, &fs,
+      result = execute_cfi (cache, fde->cie, &fs, true,
                            fde->instructions, fde->instructions_end, false,
                            fde->start, address);
       if (likely (result == DWARF_E_NOERROR))
@@ -505,6 +513,112 @@ __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde,
   return result;
 }
 
+ptrdiff_t
+dwarf_cfi_frames (Dwarf_CFI *cache, ptrdiff_t offset,
+                 void **state, Dwarf_Frame **framep)
+{
+  if (cache == NULL)
+    return -1;
+
+  if (offset < 0)
+    {
+      /* Special case call for cleanup.  */
+
+      if (*state != NULL)
+       {
+         clear_prev (*state);
+         free (*state);
+         *state = NULL;
+       }
+      return 0;
+    }
+
+  Dwarf_Frame *fs = *state;
+
+  struct dwarf_fde *fde;
+  if (offset == 0)
+    {
+      /* Start at the beginning.  */
+      assert (fs == NULL);
+      fde = cache->first_fde ?: (void *) -1l;
+    }
+  else
+    {
+      /* Resume from the last iteration.  */
+      assert (fs != NULL);
+      fde = fs->fde;
+      if (fs->fde_pc == fde->instructions_end)
+       {
+         /* We've hit the end of this FDE.  Move to the next one.  */
+         clear_prev (fs);
+         free (fs);
+         *state = fs = NULL;
+         fde = fde->next;
+       }
+    }
+
+  if (fs == NULL)
+    {
+      /* We're starting fresh on a new FDE.  */
+
+      if (fde == (void *) -1l)
+       {
+         /* No cached next FDE.  We have to intern the next one.  */
+
+         fde = __libdw_fde_by_offset (cache, offset, &offset);
+         if (fde == (void *) -1l)
+           /* End of the line.  */
+           return 0;
+       }
+
+      /* Start from this FDE's CIE's initial state.  */
+      int result = cie_cache_initial_state (cache, fde->cie);
+      if (likely (result == DWARF_E_NOERROR))
+       {
+         fs = duplicate_frame_state (fde->cie->initial_state, NULL);
+         if (unlikely (fs == NULL))
+           result = DWARF_E_NOMEM;
+       }
+      if (unlikely (result != DWARF_E_NOERROR))
+       {
+         __libdw_seterrno (result);
+         return -1;
+       }
+
+      fs->fde_pc = fde->instructions;
+      *state = fs;
+    }
+
+  /* Now play forward from the last position in the FDE.  */
+
+  assert (fs->fde == fde);
+  assert (fs->fde_pc < fde->instructions_end);
+  int result = execute_cfi (cache, fde->cie, &fs, false,
+                           fs->fde_pc, fde->instructions_end, false,
+                           fs->end, 0);
+  if (likely (result == DWARF_E_NOERROR))
+    {
+      *framep = duplicate_frame_state (fs, NULL);
+      if (unlikely (*framep == NULL))
+       {
+         clear_prev (fs);
+         free (fs);
+         fs = NULL;
+         result = DWARF_E_NOMEM;
+       }
+    }
+
+  *state = fs;
+
+  if (unlikely (result != DWARF_E_NOERROR))
+    {
+      __libdw_seterrno (result);
+      offset = -1;
+    }
+
+  return offset;
+}
+
 int
 dwarf_cfi_validate_fde (cache, offset, start, end, signalp, encoding)
      Dwarf_CFI *cache;
@@ -517,7 +631,7 @@ dwarf_cfi_validate_fde (cache, offset, start, end, signalp, encoding)
   if (cache == NULL)
     return -1;
 
-  struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
+  struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset, NULL);
   if (unlikely (fde == NULL))
     return -1;
 
index ef9cd7e1b37f9aaa090fa113f7ffaafaf54621b1..b300b73df08b12c09732c6c633675b8a6ceae82f 100644 (file)
@@ -82,6 +82,7 @@ struct dwarf_cie
 struct dwarf_fde
 {
   struct dwarf_cie *cie;
+  struct dwarf_fde *next;      /* Chain from cie->first_fde.  */
 
   /* This FDE describes PC values in [start, end).  */
   Dwarf_Addr start;
@@ -116,6 +117,11 @@ struct Dwarf_CFI_s
   /* Search tree for the FDEs, indexed by PC address.  */
   void *fde_tree;
 
+  /* Linked list of FDEs read so far, in file order.
+     fde_tailp will get the FDE at or after next_offset.  */
+  struct dwarf_fde *first_fde;
+  struct dwarf_fde **fde_tailp;
+
   /* Search tree for parsed DWARF expressions, indexed by raw pointer.  */
   void *expr_tree;
 
@@ -189,6 +195,9 @@ struct Dwarf_Frame_s
      which has the return_address_register and signal_frame flag.  */
   struct dwarf_fde *fde;
 
+  /* Next instruction in that FDE to execute after this state.  */
+  const uint8_t *fde_pc;
+
   /* The CFA is unknown, is R+N, or is computed by a DWARF expression.
      A bogon in the CFI can indicate an invalid/incalculable rule.
      We store that as cfa_invalid rather than barfing when processing it,
@@ -229,7 +238,8 @@ extern struct dwarf_fde *__libdw_find_fde (Dwarf_CFI *cache,
 
 /* Look for an FDE by its offset in the section.  */
 extern struct dwarf_fde *__libdw_fde_by_offset (Dwarf_CFI *cache,
-                                               Dwarf_Off offset)
+                                               Dwarf_Off offset,
+                                               ptrdiff_t *next_offset)
   __nonnull_attribute__ (1) internal_function;
 
 /* Process the FDE that contains the given PC address,
index c935631ebf304e08cfa01bf46d7d71ea55fb007e..91e179f370cf398a6673e1a86bb1214bb9a07bda 100644 (file)
@@ -1,5 +1,5 @@
 /* Get CFI from DWARF file.
-   Copyright (C) 2009 Red Hat, Inc.
+   Copyright (C) 2009-2010 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -83,6 +83,8 @@ dwarf_getcfi (dbg)
 
       cfi->next_offset = 0;
       cfi->cie_tree = cfi->fde_tree = cfi->expr_tree = NULL;
+      cfi->first_fde = NULL;
+      cfi->fde_tailp = &cfi->first_fde;
 
       cfi->ebl = NULL;
 
index 64a2a885525237507bd544db6ce4660d553738d9..50015202e19fe9bec98482fdd27560dc31851fd4 100644 (file)
@@ -87,6 +87,8 @@ allocate_cfi (Elf *elf, GElf_Addr vaddr)
   cfi->textrel = 0;            /* XXX ? */
   cfi->datarel = 0;            /* XXX ? */
 
+  cfi->fde_tailp = &cfi->first_fde;
+
   return cfi;
 }
 
index 5685252ba01bc056133f308afb5848206cb93e43..14e9395daf0678e6cff181d747d89b628910e738 100644 (file)
@@ -98,6 +98,8 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
       return NULL;
     }
 
+  fde->next = NULL;
+
   fde->instructions = entry->start;
   fde->instructions_end = entry->end;
   if (unlikely (read_encoded_value (cache, cie->fde_encoding,
@@ -141,32 +143,54 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
 
 struct dwarf_fde *
 internal_function
-__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
+__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset, ptrdiff_t *nextoff)
 {
   Dwarf_CFI_Entry entry;
-  Dwarf_Off next_offset;
-  int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
-                                      &cache->data->d, CFI_IS_EH (cache),
-                                      offset, &next_offset, &entry);
-  if (result != 0)
+  while (1)
     {
-      if (result > 0)
-      invalid:
-       __libdw_seterrno (DWARF_E_INVALID_DWARF);
-      return NULL;
-    }
+      Dwarf_Off next_offset;
+      int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
+                                          &cache->data->d, CFI_IS_EH (cache),
+                                          offset, &next_offset, &entry);
+      if (result != 0)
+       {
+         if (result > 0)
+           {
+             if (nextoff != NULL)
+               return (void *) -1l;
+           invalid:
+             __libdw_seterrno (DWARF_E_INVALID_DWARF);
+           }
+         return NULL;
+       }
+
+      /* If this happened to be what we would have read next, notice it.  */
+      if (cache->next_offset == offset)
+       cache->next_offset = next_offset;
+
+      offset = next_offset;
+
+      if (!dwarf_cfi_cie_p (&entry))
+       {
+         if (nextoff != NULL)
+           *nextoff = next_offset;
+         break;
+       }
 
-  if (unlikely (dwarf_cfi_cie_p (&entry)))
-    goto invalid;
+      if (nextoff == NULL)
+       goto invalid;
+    }
 
   /* We have a new FDE to consider.  */
   struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
   if (fde == (void *) -1l || fde == NULL)
     return NULL;
 
-  /* If this happened to be what we would have read next, notice it.  */
-  if (cache->next_offset == offset)
-    cache->next_offset = next_offset;
+  if (offset == cache->next_offset)
+    {
+      *cache->fde_tailp = fde;
+      cache->fde_tailp = &fde->next;
+    }
 
   return fde;
 }
@@ -251,7 +275,7 @@ __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
       Dwarf_Off offset = binary_search_fde (cache, address);
       if (offset == (Dwarf_Off) -1l)
        goto no_match;
-      struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
+      struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset, NULL);
       if (unlikely (fde != NULL)
          /* Sanity check the address range.  */
          && unlikely (address < fde->start || address >= fde->end))
index d3e7a5abe028c6e40fafb91ac802f8201462e54e..67eea1329faf82a7ea63135bc9594cfe5943d1f3 100644 (file)
@@ -780,6 +780,20 @@ extern int dwarf_cfi_addrframe (Dwarf_CFI *cache,
                                Dwarf_Addr address, Dwarf_Frame **frame)
   __nonnull_attribute__ (3);
 
+/* Iterate through the distinct CFI states.  In the first call, OFFSET
+   is zero and *STATE is null.  Each subsequent call gets the previous
+   call's return value as OFFSET and *STATE as the previous call left
+   it.  Returns -1 for errors, or 0 when there are no more states to
+   fetch; these cases clean up *STATE and leave it null.  A positive
+   return value leaves some iteration state in *STATE and fills *FRAME
+   with the malloc'd pointer for one distinct CFI state.  To abort
+   iteration before getting a nonpositive return value, pass -1 as
+   OFFSET to clean up *STATE and leave it null.  */
+extern ptrdiff_t dwarf_cfi_frames (Dwarf_CFI *cache, ptrdiff_t offset,
+                                  void **state, Dwarf_Frame **frame)
+  __nonnull_attribute__ (3, 4);
+
+
 /* Return the DWARF register number used in FRAME to denote
    the return address in FRAME's caller frame.  The remaining
    arguments can be non-null to fill in more information.