]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/dwarf2-frame.c
This commit was manufactured by cvs2svn to create tag 'carlton-
[thirdparty/binutils-gdb.git] / gdb / dwarf2-frame.c
index 2b35dd782e892c06f733432b9eee7efdfaced37c..6e420cdb89ea8ee51c36c97c5567f192c9625619 100644 (file)
@@ -36,6 +36,7 @@
 #include "gdb_assert.h"
 #include "gdb_string.h"
 
+#include "complaints.h"
 #include "dwarf2-frame.h"
 
 /* Call Frame Information (CFI).  */
@@ -65,6 +66,9 @@ struct dwarf2_cie
   /* Encoding of addresses.  */
   unsigned char encoding;
 
+  /* True if a 'z' augmentation existed.  */
+  unsigned char saw_z_augmentation;
+
   struct dwarf2_cie *next;
 };
 
@@ -93,6 +97,28 @@ static struct dwarf2_fde *dwarf2_frame_find_fde (CORE_ADDR *pc);
 
 /* Structure describing a frame state.  */
 
+enum dwarf2_reg_rule
+{
+  /* Make certain that 0 maps onto the correct enum value - the
+     corresponding structure is being initialized using memset zero.
+     This indicates that CFI didn't provide any information at all
+     about a register - leaving how to obtain it's value totally
+     unspecified.  */
+  REG_UNSPECIFIED = 0,
+  /* The term "undefined" comes from the DWARF2 CFI spec which this
+     code is moddeling - it indicates that the register's value is
+     "undefined".  */
+  /* NOTE: cagney/2003-09-08: GCC uses the less formal term "unsaved"
+     - it's definition is a combination of REG_UNDEFINED and
+     REG_UNSPECIFIED - the failure to differentiate the two helps
+     explain a few problems with the CFI GCC outputs.  */
+  REG_UNDEFINED,
+  REG_SAVED_OFFSET,
+  REG_SAVED_REG,
+  REG_SAVED_EXP,
+  REG_SAME_VALUE
+};
+
 struct dwarf2_frame_state
 {
   /* Each register save state can be described in terms of a CFA slot,
@@ -107,13 +133,7 @@ struct dwarf2_frame_state
        unsigned char *exp;
       } loc;
       ULONGEST exp_len;
-      enum {
-       REG_UNSAVED,
-       REG_SAVED_OFFSET,
-       REG_SAVED_REG,
-       REG_SAVED_EXP,
-       REG_UNMODIFIED
-      } how;
+      enum dwarf2_reg_rule how;
     } *reg;
     int num_regs;
 
@@ -163,7 +183,7 @@ dwarf2_frame_state_alloc_regs (struct dwarf2_frame_state_reg_info *rs,
     xrealloc (rs->reg, num_regs * size);
 
   /* Initialize newly allocated registers.  */
-  memset (rs->reg + rs->num_regs * size, 0, (num_regs - rs->num_regs) * size);
+  memset (rs->reg + rs->num_regs, 0, (num_regs - rs->num_regs) * size);
   rs->num_regs = num_regs;
 }
 
@@ -350,19 +370,20 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end,
            case DW_CFA_undefined:
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
              dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
-             fs->regs.reg[reg].how = REG_UNSAVED;
+             fs->regs.reg[reg].how = REG_UNDEFINED;
              break;
 
            case DW_CFA_same_value:
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
              dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
-             fs->regs.reg[reg].how = REG_UNMODIFIED;
+             fs->regs.reg[reg].how = REG_SAME_VALUE;
              break;
 
            case DW_CFA_register:
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp);
              dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
+             fs->regs.reg[reg].how = REG_SAVED_REG;
              fs->regs.reg[reg].loc.reg = utmp;
              break;
 
@@ -452,15 +473,14 @@ struct dwarf2_frame_cache
   struct dwarf2_frame_state_reg *reg;
 };
 
-struct dwarf2_frame_cache *
+static struct dwarf2_frame_cache *
 dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache)
 {
   struct cleanup *old_chain;
-  int num_regs = NUM_REGS + NUM_PSEUDO_REGS;
+  const int num_regs = NUM_REGS + NUM_PSEUDO_REGS;
   struct dwarf2_frame_cache *cache;
   struct dwarf2_frame_state *fs;
   struct dwarf2_fde *fde;
-  int reg;
 
   if (*this_cache)
     return *this_cache;
@@ -487,15 +507,12 @@ dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache)
      done for "normal" frames and not for resume-type frames (signal
      handlers, sentinel frames, dummy frames).
 
-     We don't do what GCC's does here (yet).  It's not clear how
-     reliable the method is.  There's also a problem with finding the
-     right FDE; see the comment in dwarf_frame_p.  If dwarf_frame_p
-     selected this frame unwinder because it found the FDE for the
-     next function, using the adjusted return address might not yield
-     a FDE at all.  The problem isn't specific to DWARF CFI; other
-     unwinders loose in similar ways.  Therefore it's probably
-     acceptable to leave things slightly broken for now.  */
-  fs->pc = frame_pc_unwind (next_frame);
+     frame_unwind_address_in_block does just this.
+
+     It's not clear how reliable the method is though - there is the
+     potential for the register state pre-call being different to that
+     on return.  */
+  fs->pc = frame_unwind_address_in_block (next_frame);
 
   /* Find the correct FDE.  */
   fde = dwarf2_frame_find_fde (&fs->pc);
@@ -534,25 +551,85 @@ dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache)
       internal_error (__FILE__, __LINE__, "Unknown CFA rule.");
     }
 
-  /* Save the register info in the cache.  */
-  for (reg = 0; reg < fs->regs.num_regs; reg++)
-    {
-      int regnum;
-
-      /* Skip the return address column.  */
-      if (reg == fs->retaddr_column)
-       continue;
-
-      /* Use the GDB register number as index.  */
-      regnum = DWARF2_REG_TO_REGNUM (reg);
+  /* Initialize things so that all registers are marked as
+     unspecified.  */
+  {
+    int regnum;
+    for (regnum = 0; regnum < num_regs; regnum++)
+      cache->reg[regnum].how = REG_UNSPECIFIED;
+  }
 
-      if (regnum >= 0 && regnum < num_regs)
-       cache->reg[regnum] = fs->regs.reg[reg];
+  /* Go through the DWARF2 CFI generated table and save its register
+     location information in the cache.  */
+  {
+    int column;                /* CFI speak for "register number".  */
+    for (column = 0; column < fs->regs.num_regs; column++)
+      {
+       int regnum;
+       
+       /* Skip the return address column.  */
+       if (column == fs->retaddr_column)
+         /* NOTE: cagney/2003-06-07: Is this right?  What if
+            RETADDR_COLUMN corresponds to a real register (and,
+            worse, that isn't the PC_REGNUM)?  I'm guessing that the
+            PC_REGNUM further down is trying to handle this.  That
+            can't be right though - PC_REGNUM may not be valid (it
+            can be -ve).  I think, instead when RETADDR_COLUM isn't a
+            real register, it should map itself onto frame_pc_unwind.  */
+         continue;
+
+       /* Use the GDB register number as the destination index.  */
+       regnum = DWARF2_REG_TO_REGNUM (column);
+
+       /* If there's no corresponding GDB register, ignore it.  */
+       if (regnum < 0 || regnum >= num_regs)
+         continue;
+
+       /* NOTE: cagney/2003-09-05: CFI should specify the disposition
+          of all debug info registers.  If it doesn't complain (but
+          not too loudly).  It turns out that GCC, assumes that an
+          unspecified register implies "same value" when CFI (draft
+          7) specifies nothing at all.  Such a register could equally
+          be interpreted as "undefined".  Also note that this check
+          isn't sufficient - it only checks that all registers in the
+          range [0 .. max column] are specified - and won't detect
+          problems when a debug info register falls outside of the
+          table.  Need a way of iterating through all the valid
+          DWARF2 register numbers.  */
+       if (fs->regs.reg[column].how == REG_UNSPECIFIED)
+         complaint (&symfile_complaints,
+                    "Incomplete CFI data; unspecified registers at 0x%s",
+                    paddr (fs->pc));
+
+       cache->reg[regnum] = fs->regs.reg[column];
+      }
+  }
+
+  /* Store the location of the return addess.  If the return address
+     column (adjusted) is not the same as gdb's PC_REGNUM, then this
+     implies a copy from the ra column register.  */
+  if (fs->retaddr_column < fs->regs.num_regs
+      && fs->regs.reg[fs->retaddr_column].how != REG_UNDEFINED)
+    {
+      /* See comment above about a possibly -ve PC_REGNUM.  If this
+         assertion fails, it's a problem with this code and not the
+         architecture.  */
+      gdb_assert (PC_REGNUM >= 0);
+      cache->reg[PC_REGNUM] = fs->regs.reg[fs->retaddr_column];
+    }
+  else
+    {
+      int reg = DWARF2_REG_TO_REGNUM (fs->retaddr_column);
+      if (reg != PC_REGNUM)
+       {
+         /* See comment above about PC_REGNUM being -ve.  If this
+            assertion fails, it's a problem with this code and not
+            the architecture.  */
+         gdb_assert (PC_REGNUM >= 0);
+         cache->reg[PC_REGNUM].loc.reg = reg;
+         cache->reg[PC_REGNUM].how = REG_SAVED_REG;
+       }
     }
-
-  /* Stored the location of the return addess.  */
-  if (fs->retaddr_column < fs->regs.num_regs)
-    cache->reg[PC_REGNUM] = fs->regs.reg[fs->retaddr_column];
 
   do_cleanups (old_chain);
 
@@ -581,7 +658,9 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache,
 
   switch (cache->reg[regnum].how)
     {
-    case REG_UNSAVED:
+    case REG_UNDEFINED:
+      /* If CFI explicitly specified that the value isn't defined,
+        mark it as optimized away - the value isn't available.  */
       *optimizedp = 1;
       *lvalp = not_lval;
       *addrp = 0;
@@ -591,6 +670,28 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache,
          /* GCC defines the CFA as the value of the stack pointer
             just before the call instruction is executed.  Do other
             compilers use the same definition?  */
+         /* DWARF V3 Draft 7 p102: Typically, the CFA is defined to
+            be the value of the stack pointer at the call site in the
+            previous frame (which may be different from its value on
+            entry to the current frame).  */
+         /* DWARF V3 Draft 7 p103: The first column of the rules
+             defines the rule which computes the CFA value; it may be
+             either a register and a signed offset that are added
+             together or a DWARF expression that is evaluated.  */
+         /* FIXME: cagney/2003-07-07: I don't understand this.  The
+             CFI info should have provided unwind information for the
+             SP register and then pointed ->cfa_reg at it, not the
+             reverse.  Assuming that SP_REGNUM is !-ve, there is a
+             very real posibility that CFA is an offset from some
+             other register, having nothing to do with the unwound SP
+             value.  */
+         /* FIXME: cagney/2003-09-05: I think I understand.  GDB was
+            lumping the two states "unspecified" and "undefined"
+            together.  Here SP_REGNUM was "unspecified", GCC assuming
+            that in such a case CFA would be used.  This branch of
+            the if statement should be deleted - the problem of
+            SP_REGNUM is now handed by the case REG_UNSPECIFIED
+            below.  */
          *optimizedp = 0;
          if (valuep)
            {
@@ -642,7 +743,59 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache,
        }
       break;
 
-    case REG_UNMODIFIED:
+    case REG_UNSPECIFIED:
+      /* GCC, in its infinite wisdom decided to not provide unwind
+        information for registers that are "same value".  Since
+        DWARF2 (3 draft 7) doesn't define such behavior, said
+        registers are actually undefined (which is different to CFI
+        "undefined").  Code above issues a complaint about this.
+        Here just fudge the books, assume GCC, and that the value is
+        more inner on the stack.  */
+      if (SP_REGNUM >= 0 && regnum == SP_REGNUM)
+       {
+         /* Can things get worse?  Yep!  One of the registers GCC
+            forgot to provide unwind information for was the stack
+            pointer.  Outch!  GCC appears to assumes that the CFA
+            address can be used - after all it points to the inner
+            most address of the previous frame before the function
+            call and that's always the same as the stack pointer on
+            return, right?  Wrong.  See GCC's i386 STDCALL option for
+            an ABI that has a different entry and return stack
+            pointer.  */
+         /* DWARF V3 Draft 7 p102: Typically, the CFA is defined to
+            be the value of the stack pointer at the call site in the
+            previous frame (which may be different from its value on
+            entry to the current frame).  */
+         /* DWARF V3 Draft 7 p103: The first column of the rules
+             defines the rule which computes the CFA value; it may be
+             either a register and a signed offset that are added
+             together or a DWARF expression that is evaluated.  */
+         /* NOTE: cagney/2003-09-05: Should issue a complain.
+             Unfortunately it turns out that DWARF2 CFI has a problem.
+             Since CFI specifies the location at which a register was
+             saved (not its value) it isn't possible to specify
+             something like "unwound(REG) == REG + constant" using CFI
+             as will almost always occure with the stack pointer.  I
+             guess CFI should be point SP at CFA.  Ref: danielj,
+             "Describing unsaved stack pointers", posted to dwarf2
+             list 2003-08-15.  */
+         *optimizedp = 0;
+         *lvalp = not_lval;
+         *addrp = 0;
+         *realnump = -1;
+         if (valuep)
+           /* Store the value.  */
+           store_typed_address (valuep, builtin_type_void_data_ptr,
+                                cache->cfa);
+       }
+      else
+       /* Assume that the register can be found in the next inner
+           most frame.  */
+       frame_register_unwind (next_frame, regnum,
+                              optimizedp, lvalp, addrp, realnump, valuep);
+      break;
+
+    case REG_SAME_VALUE:
       frame_register_unwind (next_frame, regnum,
                             optimizedp, lvalp, addrp, realnump, valuep);
       break;
@@ -660,16 +813,13 @@ static const struct frame_unwind dwarf2_frame_unwind =
 };
 
 const struct frame_unwind *
-dwarf2_frame_p (CORE_ADDR pc)
+dwarf2_frame_sniffer (struct frame_info *next_frame)
 {
-  /* The way GDB works, this function can be called with PC just after
-     the last instruction of the function we're supposed to return the
-     unwind methods for.  In that case we won't find the correct FDE;
-     instead we find the FDE for the next function, or we won't find
-     an FDE at all.  There is a possible solution (see the comment in
-     dwarf2_frame_cache), GDB doesn't pass us enough information to
-     implement it.  */
-  if (dwarf2_frame_find_fde (&pc))
+  /* Grab an address that is guarenteed to reside somewhere within the
+     function.  frame_pc_unwind(), for a no-return next function, can
+     end up returning something past the end of this function's body.  */
+  CORE_ADDR block_addr = frame_unwind_address_in_block (next_frame);
+  if (dwarf2_frame_find_fde (&block_addr))
     return &dwarf2_frame_unwind;
 
   return NULL;
@@ -700,8 +850,9 @@ static const struct frame_base dwarf2_frame_base =
 };
 
 const struct frame_base *
-dwarf2_frame_base_p (CORE_ADDR pc)
+dwarf2_frame_base_sniffer (struct frame_info *next_frame)
 {
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
   if (dwarf2_frame_find_fde (&pc))
     return &dwarf2_frame_base;
 
@@ -732,8 +883,16 @@ struct comp_unit
 
   /* Pointer to the .debug_frame section.  */
   asection *dwarf_frame_section;
+
+  /* Base for DW_EH_PE_datarel encodings.  */
+  bfd_vma dbase;
+
+  /* Base for DW_EH_PE_textrel encodings.  */
+  bfd_vma tbase;
 };
 
+const struct objfile_data *dwarf2_frame_data;
+
 static unsigned int
 read_1_byte (bfd *bfd, char *buf)
 {
@@ -884,6 +1043,8 @@ static CORE_ADDR
 read_encoded_value (struct comp_unit *unit, unsigned char encoding,
                    char *buf, unsigned int *bytes_read_ptr)
 {
+  int ptr_len = size_of_encoded_value (DW_EH_PE_absptr);
+  ptrdiff_t offset;
   CORE_ADDR base;
 
   /* GCC currently doesn't generate DW_EH_PE_indirect encodings for
@@ -892,6 +1053,8 @@ read_encoded_value (struct comp_unit *unit, unsigned char encoding,
     internal_error (__FILE__, __LINE__, 
                    "Unsupported encoding: DW_EH_PE_indirect");
 
+  *bytes_read_ptr = 0;
+
   switch (encoding & 0x70)
     {
     case DW_EH_PE_absptr:
@@ -901,32 +1064,47 @@ read_encoded_value (struct comp_unit *unit, unsigned char encoding,
       base = bfd_get_section_vma (unit->bfd, unit->dwarf_frame_section);
       base += (buf - unit->dwarf_frame_buffer);
       break;
+    case DW_EH_PE_datarel:
+      base = unit->dbase;
+      break;
+    case DW_EH_PE_textrel:
+      base = unit->tbase;
+      break;
+    case DW_EH_PE_aligned:
+      base = 0;
+      offset = buf - unit->dwarf_frame_buffer;
+      if ((offset % ptr_len) != 0)
+       {
+         *bytes_read_ptr = ptr_len - (offset % ptr_len);
+         buf += *bytes_read_ptr;
+       }
+      break;
     default:
       internal_error (__FILE__, __LINE__, "Invalid or unsupported encoding");
     }
 
   if ((encoding & 0x0f) == 0x00)
-    encoding |= encoding_for_size (TYPE_LENGTH(builtin_type_void_data_ptr));
+    encoding |= encoding_for_size (ptr_len);
 
   switch (encoding & 0x0f)
     {
     case DW_EH_PE_udata2:
-      *bytes_read_ptr = 2;
+      *bytes_read_ptr += 2;
       return (base + bfd_get_16 (unit->abfd, (bfd_byte *) buf));
     case DW_EH_PE_udata4:
-      *bytes_read_ptr = 4;
+      *bytes_read_ptr += 4;
       return (base + bfd_get_32 (unit->abfd, (bfd_byte *) buf));
     case DW_EH_PE_udata8:
-      *bytes_read_ptr = 8;
+      *bytes_read_ptr += 8;
       return (base + bfd_get_64 (unit->abfd, (bfd_byte *) buf));
     case DW_EH_PE_sdata2:
-      *bytes_read_ptr = 2;
+      *bytes_read_ptr += 2;
       return (base + bfd_get_signed_16 (unit->abfd, (bfd_byte *) buf));
     case DW_EH_PE_sdata4:
-      *bytes_read_ptr = 4;
+      *bytes_read_ptr += 4;
       return (base + bfd_get_signed_32 (unit->abfd, (bfd_byte *) buf));
     case DW_EH_PE_sdata8:
-      *bytes_read_ptr = 8;
+      *bytes_read_ptr += 8;
       return (base + bfd_get_signed_64 (unit->abfd, (bfd_byte *) buf));
     default:
       internal_error (__FILE__, __LINE__, "Invalid or unsupported encoding");
@@ -973,9 +1151,13 @@ dwarf2_frame_find_fde (CORE_ADDR *pc)
       struct dwarf2_fde *fde;
       CORE_ADDR offset;
 
+      fde = objfile_data (objfile, dwarf2_frame_data);
+      if (fde == NULL)
+       continue;
+
+      gdb_assert (objfile->section_offsets);
       offset = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
-      
-      fde = objfile->sym_private;
+
       while (fde)
        {
          if (*pc >= fde->initial_location + offset
@@ -995,8 +1177,8 @@ dwarf2_frame_find_fde (CORE_ADDR *pc)
 static void
 add_fde (struct comp_unit *unit, struct dwarf2_fde *fde)
 {
-  fde->next = unit->objfile->sym_private;
-  unit->objfile->sym_private = fde;
+  fde->next = objfile_data (unit->objfile, dwarf2_frame_data);
+  set_objfile_data (unit->objfile, dwarf2_frame_data, fde);
 }
 
 #ifdef CC_HAS_LONG_LONG
@@ -1005,35 +1187,44 @@ add_fde (struct comp_unit *unit, struct dwarf2_fde *fde)
 #define DW64_CIE_ID ~0
 #endif
 
-/* Read a CIE or FDE in BUF and decode it.  */
+static char *decode_frame_entry (struct comp_unit *unit, char *start,
+                                int eh_frame_p);
 
+/* Decode the next CIE or FDE.  Return NULL if invalid input, otherwise
+   the next byte to be processed.  */
 static char *
-decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
+decode_frame_entry_1 (struct comp_unit *unit, char *start, int eh_frame_p)
 {
+  char *buf;
   LONGEST length;
   unsigned int bytes_read;
-  int dwarf64_p = 0;
-  ULONGEST cie_id = DW_CIE_ID;
+  int dwarf64_p;
+  ULONGEST cie_id;
   ULONGEST cie_pointer;
-  char *start = buf;
   char *end;
 
+  buf = start;
   length = read_initial_length (unit->abfd, buf, &bytes_read);
   buf += bytes_read;
   end = buf + length;
 
+  /* Are we still within the section? */
+  if (end > unit->dwarf_frame_buffer + unit->dwarf_frame_size)
+    return NULL;
+
   if (length == 0)
     return end;
 
-  if (bytes_read == 12)
-    dwarf64_p = 1;
+  /* Distinguish between 32 and 64-bit encoded frame info.  */
+  dwarf64_p = (bytes_read == 12);
 
-  /* In a .eh_frame section, zero is used to distinguish CIEs from
-     FDEs.  */
+  /* In a .eh_frame section, zero is used to distinguish CIEs from FDEs.  */
   if (eh_frame_p)
     cie_id = 0;
   else if (dwarf64_p)
     cie_id = DW64_CIE_ID;
+  else
+    cie_id = DW_CIE_ID;
 
   if (dwarf64_p)
     {
@@ -1071,7 +1262,8 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
       cie->encoding = encoding_for_size (unit->addr_size);
 
       /* Check version number.  */
-      gdb_assert (read_1_byte (unit->abfd, buf) == DW_CIE_VERSION);
+      if (read_1_byte (unit->abfd, buf) != DW_CIE_VERSION)
+       return NULL;
       buf += 1;
 
       /* Interpret the interesting bits of the augmentation.  */
@@ -1099,12 +1291,15 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
       cie->return_address_register = read_1_byte (unit->abfd, buf);
       buf += 1;
 
-      if (*augmentation == 'z')
+      cie->saw_z_augmentation = (*augmentation == 'z');
+      if (cie->saw_z_augmentation)
        {
          ULONGEST length;
 
          length = read_unsigned_leb128 (unit->abfd, buf, &bytes_read);
          buf += bytes_read;
+         if (buf > end)
+           return NULL;
          cie->initial_instructions = buf + length;
          augmentation++;
        }
@@ -1157,16 +1352,20 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
       /* This is a FDE.  */
       struct dwarf2_fde *fde;
 
+      /* In an .eh_frame section, the CIE pointer is the delta between the
+        address within the FDE where the CIE pointer is stored and the
+        address of the CIE.  Convert it to an offset into the .eh_frame
+        section.  */
       if (eh_frame_p)
        {
-         /* In an .eh_frame section, the CIE pointer is the delta
-             between the address within the FDE where the CIE pointer
-             is stored and the address of the CIE.  Convert it to an
-             offset into the .eh_frame section.  */
          cie_pointer = buf - unit->dwarf_frame_buffer - cie_pointer;
          cie_pointer -= (dwarf64_p ? 8 : 4);
        }
 
+      /* In either case, validate the result is still within the section.  */
+      if (cie_pointer >= unit->dwarf_frame_size)
+       return NULL;
+
       fde = (struct dwarf2_fde *)
        obstack_alloc (&unit->objfile->psymbol_obstack,
                       sizeof (struct dwarf2_fde));
@@ -1188,6 +1387,20 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
        read_encoded_value (unit, fde->cie->encoding & 0x0f, buf, &bytes_read);
       buf += bytes_read;
 
+      /* A 'z' augmentation in the CIE implies the presence of an
+        augmentation field in the FDE as well.  The only thing known
+        to be in here at present is the LSDA entry for EH.  So we
+        can skip the whole thing.  */
+      if (fde->cie->saw_z_augmentation)
+       {
+         ULONGEST length;
+
+         length = read_unsigned_leb128 (unit->abfd, buf, &bytes_read);
+         buf += bytes_read + length;
+         if (buf > end)
+           return NULL;
+       }
+
       fde->instructions = buf;
       fde->end = end;
 
@@ -1196,6 +1409,99 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
 
   return end;
 }
+
+/* Read a CIE or FDE in BUF and decode it.  */
+static char *
+decode_frame_entry (struct comp_unit *unit, char *start, int eh_frame_p)
+{
+  enum { NONE, ALIGN4, ALIGN8, FAIL } workaround = NONE;
+  char *ret;
+  const char *msg;
+  ptrdiff_t start_offset;
+
+  while (1)
+    {
+      ret = decode_frame_entry_1 (unit, start, eh_frame_p);
+      if (ret != NULL)
+       break;
+
+      /* We have corrupt input data of some form.  */
+
+      /* ??? Try, weakly, to work around compiler/assembler/linker bugs
+        and mismatches wrt padding and alignment of debug sections.  */
+      /* Note that there is no requirement in the standard for any
+        alignment at all in the frame unwind sections.  Testing for
+        alignment before trying to interpret data would be incorrect.
+
+        However, GCC traditionally arranged for frame sections to be
+        sized such that the FDE length and CIE fields happen to be
+        aligned (in theory, for performance).  This, unfortunately,
+        was done with .align directives, which had the side effect of
+        forcing the section to be aligned by the linker.
+
+        This becomes a problem when you have some other producer that
+        creates frame sections that are not as strictly aligned.  That
+        produces a hole in the frame info that gets filled by the 
+        linker with zeros.
+
+        The GCC behaviour is arguably a bug, but it's effectively now
+        part of the ABI, so we're now stuck with it, at least at the
+        object file level.  A smart linker may decide, in the process
+        of compressing duplicate CIE information, that it can rewrite
+        the entire output section without this extra padding.  */
+
+      start_offset = start - unit->dwarf_frame_buffer;
+      if (workaround < ALIGN4 && (start_offset & 3) != 0)
+       {
+         start += 4 - (start_offset & 3);
+         workaround = ALIGN4;
+         continue;
+       }
+      if (workaround < ALIGN8 && (start_offset & 7) != 0)
+       {
+         start += 8 - (start_offset & 7);
+         workaround = ALIGN8;
+         continue;
+       }
+
+      /* Nothing left to try.  Arrange to return as if we've consumed
+        the entire input section.  Hopefully we'll get valid info from
+        the other of .debug_frame/.eh_frame.  */
+      workaround = FAIL;
+      ret = unit->dwarf_frame_buffer + unit->dwarf_frame_size;
+      break;
+    }
+
+  switch (workaround)
+    {
+    case NONE:
+      break;
+
+    case ALIGN4:
+      complaint (&symfile_complaints,
+                "Corrupt data in %s:%s; align 4 workaround apparently succeeded",
+                unit->dwarf_frame_section->owner->filename,
+                unit->dwarf_frame_section->name);
+      break;
+
+    case ALIGN8:
+      complaint (&symfile_complaints,
+                "Corrupt data in %s:%s; align 8 workaround apparently succeeded",
+                unit->dwarf_frame_section->owner->filename,
+                unit->dwarf_frame_section->name);
+      break;
+
+    default:
+      complaint (&symfile_complaints,
+                "Corrupt data in %s:%s",
+                unit->dwarf_frame_section->owner->filename,
+                unit->dwarf_frame_section->name);
+      break;
+    }
+
+  return ret;
+}
+
 \f
 
 /* FIXME: kettenis/20030504: This still needs to be integrated with
@@ -1223,11 +1529,15 @@ dwarf2_build_frame_info (struct objfile *objfile)
   unit.abfd = objfile->obfd;
   unit.objfile = objfile;
   unit.addr_size = objfile->obfd->arch_info->bits_per_address / 8;
+  unit.dbase = 0;
+  unit.tbase = 0;
 
   /* First add the information from the .eh_frame section.  That way,
      the FDEs from that section are searched last.  */
   if (dwarf_eh_frame_offset)
     {
+      asection *got, *txt;
+
       unit.cie = NULL;
       unit.dwarf_frame_buffer = dwarf2_read_section (objfile,
                                                     dwarf_eh_frame_offset,
@@ -1237,6 +1547,20 @@ dwarf2_build_frame_info (struct objfile *objfile)
       unit.dwarf_frame_size = dwarf_eh_frame_size;
       unit.dwarf_frame_section = dwarf_eh_frame_section;
 
+      /* FIXME: kettenis/20030602: This is the DW_EH_PE_datarel base
+        that is used for the i386/amd64 target, which currently is
+        the only target in GCC that supports/uses the
+        DW_EH_PE_datarel encoding.  */
+      got = bfd_get_section_by_name (unit.abfd, ".got");
+      if (got)
+       unit.dbase = got->vma;
+
+      /* GCC emits the DW_EH_PE_textrel encoding type on sh and ia64
+         so far.  */
+      txt = bfd_get_section_by_name (unit.abfd, ".text");
+      if (txt)
+       unit.tbase = txt->vma;
+
       frame_ptr = unit.dwarf_frame_buffer;
       while (frame_ptr < unit.dwarf_frame_buffer + unit.dwarf_frame_size)
        frame_ptr = decode_frame_entry (&unit, frame_ptr, 1);
@@ -1257,3 +1581,12 @@ dwarf2_build_frame_info (struct objfile *objfile)
        frame_ptr = decode_frame_entry (&unit, frame_ptr, 0);
     }
 }
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+void _initialize_dwarf2_frame (void);
+
+void
+_initialize_dwarf2_frame (void)
+{
+  dwarf2_frame_data = register_objfile_data ();
+}