bfd/
[deliverable/binutils-gdb.git] / gdb / dwarf2-frame.c
index 3f7fbfe047cdb5336832303a07ef53f3e96c7bda..fd4002c476cd3fc7413e03eb2a8c33e7b15da81c 100644 (file)
@@ -1,6 +1,6 @@
 /* Frame unwinder for frames with DWARF Call Frame Information.
 
-   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
 
    Contributed by Mark Kettenis.
 
@@ -32,6 +32,7 @@
 #include "symtab.h"
 #include "objfiles.h"
 #include "regcache.h"
+#include "value.h"
 
 #include "gdb_assert.h"
 #include "gdb_string.h"
@@ -63,12 +64,21 @@ struct dwarf2_cie
   gdb_byte *initial_instructions;
   gdb_byte *end;
 
+  /* Saved augmentation, in case it's needed later.  */
+  char *augmentation;
+
   /* Encoding of addresses.  */
   gdb_byte encoding;
 
   /* True if a 'z' augmentation existed.  */
   unsigned char saw_z_augmentation;
 
+  /* True if an 'S' augmentation existed.  */
+  unsigned char signal_frame;
+
+  /* The version recorded in the CIE.  */
+  unsigned char version;
+
   struct dwarf2_cie *next;
 };
 
@@ -134,6 +144,16 @@ struct dwarf2_frame_state
   LONGEST data_align;
   ULONGEST code_align;
   ULONGEST retaddr_column;
+
+  /* Flags for known producer quirks.  */
+
+  /* The ARM compilers, in DWARF2 mode, assume that DW_CFA_def_cfa
+     and DW_CFA_def_cfa_offset takes a factored offset.  */
+  int armcc_cfa_offsets_sf;
+
+  /* The ARM compilers, in DWARF2 or DWARF3 mode, may assume that
+     the CFA is defined as REG - OFFSET rather than REG + OFFSET.  */
+  int armcc_cfa_offsets_reversed;
 };
 
 /* Store the length the expression for the CFA in the `cfa_reg' field,
@@ -218,7 +238,13 @@ read_reg (void *baton, int reg)
 
   buf = alloca (register_size (gdbarch, regnum));
   frame_unwind_register (next_frame, regnum, buf);
-  return extract_typed_address (buf, builtin_type_void_data_ptr);
+
+  /* Convert the register to an integer.  This returns a LONGEST
+     rather than a CORE_ADDR, but unpack_pointer does the same thing
+     under the covers, and this makes more sense for non-pointer
+     registers.  Maybe read_reg and the associated interfaces should
+     deal with "struct value" instead of CORE_ADDR.  */
+  return unpack_long (register_type (gdbarch, regnum), buf);
 }
 
 static void
@@ -420,6 +446,10 @@ bad CFI data; mismatched DW_CFA_restore_state at 0x%s"), paddr (fs->pc));
            case DW_CFA_def_cfa:
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &fs->cfa_reg);
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp);
+
+             if (fs->armcc_cfa_offsets_sf)
+               utmp *= fs->data_align;
+
              fs->cfa_offset = utmp;
              fs->cfa_how = CFA_REG_OFFSET;
              break;
@@ -434,6 +464,10 @@ bad CFI data; mismatched DW_CFA_restore_state at 0x%s"), paddr (fs->pc));
 
            case DW_CFA_def_cfa_offset:
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp);
+
+             if (fs->armcc_cfa_offsets_sf)
+               utmp *= fs->data_align;
+
              fs->cfa_offset = utmp;
              /* cfa_how deliberately not set.  */
              break;
@@ -471,6 +505,34 @@ bad CFI data; mismatched DW_CFA_restore_state at 0x%s"), paddr (fs->pc));
              fs->regs.reg[reg].loc.offset = offset;
              break;
 
+           case DW_CFA_val_offset:
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
+             dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp);
+             offset = utmp * fs->data_align;
+             fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_VAL_OFFSET;
+             fs->regs.reg[reg].loc.offset = offset;
+             break;
+
+           case DW_CFA_val_offset_sf:
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
+             dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
+             insn_ptr = read_sleb128 (insn_ptr, insn_end, &offset);
+             offset *= fs->data_align;
+             fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_VAL_OFFSET;
+             fs->regs.reg[reg].loc.offset = offset;
+             break;
+
+           case DW_CFA_val_expression:
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
+             dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp);
+             fs->regs.reg[reg].loc.exp = insn_ptr;
+             fs->regs.reg[reg].exp_len = utmp;
+             fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_VAL_EXP;
+             insn_ptr += utmp;
+             break;
+
            case DW_CFA_def_cfa_sf:
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &fs->cfa_reg);
              if (eh_frame_p)
@@ -517,6 +579,17 @@ bad CFI data; mismatched DW_CFA_restore_state at 0x%s"), paddr (fs->pc));
              insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp);
              break;
 
+           case DW_CFA_GNU_negative_offset_extended:
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &reg);
+             if (eh_frame_p)
+               reg = dwarf2_frame_eh_frame_regnum (gdbarch, reg);
+             insn_ptr = read_uleb128 (insn_ptr, insn_end, &offset);
+             offset *= fs->data_align;
+             dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1);
+             fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_OFFSET;
+             fs->regs.reg[reg].loc.offset = -offset;
+             break;
+
            default:
              internal_error (__FILE__, __LINE__, _("Unknown CFI encountered."));
            }
@@ -677,6 +750,49 @@ dwarf2_frame_eh_frame_regnum (struct gdbarch *gdbarch, int regnum)
     return regnum;
   return ops->eh_frame_regnum (gdbarch, regnum);
 }
+
+static void
+dwarf2_frame_find_quirks (struct dwarf2_frame_state *fs,
+                         struct dwarf2_fde *fde)
+{
+  static const char *arm_idents[] = {
+    "ARM C Compiler, ADS",
+    "Thumb C Compiler, ADS",
+    "ARM C++ Compiler, ADS",
+    "Thumb C++ Compiler, ADS",
+    "ARM/Thumb C/C++ Compiler, RVCT"
+  };
+  int i;
+
+  struct symtab *s;
+
+  s = find_pc_symtab (fs->pc);
+  if (s == NULL || s->producer == NULL)
+    return;
+
+  for (i = 0; i < ARRAY_SIZE (arm_idents); i++)
+    if (strncmp (s->producer, arm_idents[i], strlen (arm_idents[i])) == 0)
+      {
+       if (fde->cie->version == 1)
+         fs->armcc_cfa_offsets_sf = 1;
+
+       if (fde->cie->version == 1)
+         fs->armcc_cfa_offsets_reversed = 1;
+
+       /* The reversed offset problem is present in some compilers
+          using DWARF3, but it was eventually fixed.  Check the ARM
+          defined augmentations, which are in the format "armcc" followed
+          by a list of one-character options.  The "+" option means
+          this problem is fixed (no quirk needed).  If the armcc
+          augmentation is missing, the quirk is needed.  */
+       if (fde->cie->version == 3
+           && (strncmp (fde->cie->augmentation, "armcc", 5) != 0
+               || strchr (fde->cie->augmentation + 5, '+') == NULL))
+         fs->armcc_cfa_offsets_reversed = 1;
+
+       return;
+      }
+}
 \f
 
 struct dwarf2_frame_cache
@@ -743,6 +859,9 @@ dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache)
   fs->code_align = fde->cie->code_alignment_factor;
   fs->retaddr_column = fde->cie->return_address_register;
 
+  /* Check for "quirks" - known bugs in producers.  */
+  dwarf2_frame_find_quirks (fs, fde);
+
   /* First decode all the insns in the CIE.  */
   execute_cfa_program (fde->cie->initial_instructions,
                       fde->cie->end, next_frame, fs, fde->eh_frame_p);
@@ -760,7 +879,10 @@ dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache)
     {
     case CFA_REG_OFFSET:
       cache->cfa = read_reg (next_frame, fs->cfa_reg);
-      cache->cfa += fs->cfa_offset;
+      if (fs->armcc_cfa_offsets_reversed)
+       cache->cfa -= fs->cfa_offset;
+      else
+       cache->cfa += fs->cfa_offset;
       break;
 
     case CFA_EXP:
@@ -955,6 +1077,28 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache,
        }
       break;
 
+    case DWARF2_FRAME_REG_SAVED_VAL_OFFSET:
+      *optimizedp = 0;
+      *lvalp = not_lval;
+      *addrp = 0;
+      *realnump = -1;
+      if (valuep)
+       store_unsigned_integer (valuep, register_size (gdbarch, regnum),
+                               cache->cfa + cache->reg[regnum].loc.offset);
+      break;
+
+    case DWARF2_FRAME_REG_SAVED_VAL_EXP:
+      *optimizedp = 0;
+      *lvalp = not_lval;
+      *addrp = 0;
+      *realnump = -1;
+      if (valuep)
+       store_unsigned_integer (valuep, register_size (gdbarch, regnum),
+                               execute_stack_op (cache->reg[regnum].loc.exp,
+                                                 cache->reg[regnum].exp_len,
+                                                 next_frame, cache->cfa));
+      break;
+
     case DWARF2_FRAME_REG_UNSPECIFIED:
       /* GCC, in its infinite wisdom decided to not provide unwind
         information for registers that are "same value".  Since
@@ -1046,15 +1190,17 @@ dwarf2_frame_sniffer (struct frame_info *next_frame)
      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))
+  struct dwarf2_fde *fde = dwarf2_frame_find_fde (&block_addr);
+  if (!fde)
     return NULL;
 
   /* On some targets, signal trampolines may have unwind information.
      We need to recognize them so that we set the frame type
      correctly.  */
 
-  if (dwarf2_frame_signal_frame_p (get_frame_arch (next_frame),
-                                  next_frame))
+  if (fde->cie->signal_frame
+      || dwarf2_frame_signal_frame_p (get_frame_arch (next_frame),
+                                     next_frame))
     return &dwarf2_signal_frame_unwind;
 
   return &dwarf2_frame_unwind;
@@ -1514,16 +1660,26 @@ decode_frame_entry_1 (struct comp_unit *unit, gdb_byte *start, int eh_frame_p)
          depends on the target address size.  */
       cie->encoding = DW_EH_PE_absptr;
 
+      /* We'll determine the final value later, but we need to
+        initialize it conservatively.  */
+      cie->signal_frame = 0;
+
       /* Check version number.  */
       cie_version = read_1_byte (unit->abfd, buf);
       if (cie_version != 1 && cie_version != 3)
        return NULL;
+      cie->version = cie_version;
       buf += 1;
 
       /* Interpret the interesting bits of the augmentation.  */
-      augmentation = (char *) buf;
+      cie->augmentation = augmentation = (char *) buf;
       buf += (strlen (augmentation) + 1);
 
+      /* Ignore armcc augmentations.  We only use them for quirks,
+        and that doesn't happen until later.  */
+      if (strncmp (augmentation, "armcc", 5) == 0)
+       augmentation += strlen (augmentation);
+
       /* The GCC 2.x "eh" augmentation has a pointer immediately
          following the augmentation string, so it must be handled
          first.  */
@@ -1597,15 +1753,23 @@ decode_frame_entry_1 (struct comp_unit *unit, gdb_byte *start, int eh_frame_p)
              augmentation++;
            }
 
-         /* Otherwise we have an unknown augmentation.
-            Bail out unless we saw a 'z' prefix.  */
-         else
+         /* "S" indicates a signal frame, such that the return
+            address must not be decremented to locate the call frame
+            info for the previous frame; it might even be the first
+            instruction of a function, so decrementing it would take
+            us to a different function.  */
+         else if (*augmentation == 'S')
            {
-             if (cie->initial_instructions == NULL)
-               return end;
+             cie->signal_frame = 1;
+             augmentation++;
+           }
 
-             /* Skip unknown augmentations.  */
-             buf = cie->initial_instructions;
+         /* Otherwise we have an unknown augmentation.  Assume that either
+            there is no augmentation data, or we saw a 'z' prefix.  */
+         else
+           {
+             if (cie->initial_instructions)
+               buf = cie->initial_instructions;
              break;
            }
        }
This page took 0.02801 seconds and 4 git commands to generate.