Blob Blame History Raw
commit 33f8fe58b9a55a0075a90cc9080a1716221a3f81
Author: Pedro Alves <palves@redhat.com>
Date:   Fri Nov 22 11:51:59 2013 +0000

    Don't let two frames with the same id end up in the frame chain.
    
    The UNWIND_SAME_ID check is done between THIS_FRAME and the next frame
    when we go try to unwind the previous frame.  But at this point, it's
    already too late -- we ended up with two frames with the same ID in
    the frame chain.  Each frame having its own ID is an invariant assumed
    throughout GDB.  This patch applies the UNWIND_SAME_ID detection
    earlier, right after the previous frame is unwound, discarding the dup
    frame if a cycle is detected.
    
    The patch includes a new test that fails before the change.  Before
    the patch, the test causes an infinite loop in GDB, after the patch,
    the UNWIND_SAME_ID logic kicks in and makes the backtrace stop with:
    
      Backtrace stopped: previous frame identical to this frame (corrupt stack?)
    
    The test uses dwarf CFI to emulate a corrupted stack with a cycle.  It
    has a function with registers marked DW_CFA_same_value (most
    importantly RSP/RIP), so that GDB computes the same ID for that frame
    and its caller.  IOW, something like this:
    
     #0 - frame_id_1
     #1 - frame_id_2
     #2 - frame_id_3
     #3 - frame_id_4
     #4 - frame_id_4  <<<< outermost (UNWIND_SAME_ID).
    
    (The test's code is just a copy of dw2-reg-undefined.S /
    dw2-reg-undefined.c, adjusted to use DW_CFA_same_value instead of
    DW_CFA_undefined, and to mark a different set of registers.)
    
    The infinite loop is here, in value_fetch_lazy:
    
          while (VALUE_LVAL (new_val) == lval_register && value_lazy (new_val))
    	{
    	  frame = frame_find_by_id (VALUE_FRAME_ID (new_val));
    ...
    	  new_val = get_frame_register_value (frame, regnum);
    	}
    
    get_frame_register_value can return a lazy register value pointing to
    the next frame.  This means that the register wasn't clobbered by
    FRAME; the debugger should therefore retrieve its value from the next
    frame.
    
    To be clear, get_frame_register_value unwinds the value in question
    from the next frame:
    
     struct value *
     get_frame_register_value (struct frame_info *frame, int regnum)
     {
       return frame_unwind_register_value (frame->next, regnum);
                                           ^^^^^^^^^^^
     }
    
    In other words, if we get a lazy lval_register, it should have the
    frame ID of the _next_ frame, never of FRAME.
    
    At this point in value_fetch_lazy, the whole relevant chunk of the
    stack up to frame #4 has already been unwound.  The loop always
    "unlazies" lval_registers in the "next/innermost" direction, not in
    the "prev/unwind further/outermost" direction.
    
    So say we're looking at frame #4.  get_frame_register_value in frame
    #4 can return a lazy register value of frame #3.  So the next
    iteration, frame_find_by_id tries to read the register from frame #3.
    But, since frame #4 happens to have same id as frame #3,
    frame_find_by_id returns frame #4 instead.  Rinse, repeat, and we have
    an infinite loop.
    
    This is an old latent problem, exposed by the recent addition of the
    frame stash.  Before we had a stash, frame_find_by_id(frame_id_4)
    would walk over all frames starting at the current frame, and would
    always find #3 first.  The stash happens to return #4 instead:
    
    struct frame_info *
    frame_find_by_id (struct frame_id id)
    {
      struct frame_info *frame, *prev_frame;
    
    ...
      /* Try using the frame stash first.  Finding it there removes the need
         to perform the search by looping over all frames, which can be very
         CPU-intensive if the number of frames is very high (the loop is O(n)
         and get_prev_frame performs a series of checks that are relatively
         expensive).  This optimization is particularly useful when this function
         is called from another function (such as value_fetch_lazy, case
         VALUE_LVAL (val) == lval_register) which already loops over all frames,
         making the overall behavior O(n^2).  */
      frame = frame_stash_find (id);
      if (frame)
        return frame;
    
      for (frame = get_current_frame (); ; frame = prev_frame)
        {
    
    gdb/
    2013-11-22  Pedro Alves  <palves@redhat.com>
    
    	PR 16155
    	* frame.c (get_prev_frame_1): Do the UNWIND_SAME_ID check between
    	this frame and the new previous frame, not between this frame and
    	the next frame.
    
    gdb/testsuite/
    2013-11-22  Pedro Alves  <palves@redhat.com>
    
    	PR 16155
    	* gdb.dwarf2/dw2-dup-frame.S: New file.
    	* gdb.dwarf2/dw2-dup-frame.c: New file.
    	* gdb.dwarf2/dw2-dup-frame.exp: New file.

Index: gdb-7.6.1/gdb/frame.c
===================================================================
--- gdb-7.6.1.orig/gdb/frame.c
+++ gdb-7.6.1/gdb/frame.c
@@ -1689,6 +1689,7 @@ get_prev_frame_1 (struct frame_info *thi
 {
   struct frame_id this_id;
   struct gdbarch *gdbarch;
+  struct frame_info *prev_frame;
 
   gdb_assert (this_frame != NULL);
   gdbarch = get_frame_arch (this_frame);
@@ -1790,22 +1791,6 @@ get_prev_frame_1 (struct frame_info *thi
 	}
     }
 
-  /* Check that this and the next frame are not identical.  If they
-     are, there is most likely a stack cycle.  As with the inner-than
-     test above, avoid comparing the inner-most and sentinel frames.  */
-  if (this_frame->level > 0
-      && frame_id_eq (this_id, get_frame_id (this_frame->next)))
-    {
-      if (frame_debug)
-	{
-	  fprintf_unfiltered (gdb_stdlog, "-> ");
-	  fprint_frame (gdb_stdlog, NULL);
-	  fprintf_unfiltered (gdb_stdlog, " // this frame has same ID }\n");
-	}
-      this_frame->stop_reason = UNWIND_SAME_ID;
-      return NULL;
-    }
-
   /* Check that this and the next frame do not unwind the PC register
      to the same memory location.  If they do, then even though they
      have different frame IDs, the new frame will be bogus; two
@@ -1853,7 +1838,31 @@ get_prev_frame_1 (struct frame_info *thi
 	}
     }
 
-  return get_prev_frame_raw (this_frame);
+  prev_frame = get_prev_frame_raw (this_frame);
+
+  /* Check that this and the prev frame are not identical.  If they
+     are, there is most likely a stack cycle.  Unlike the tests above,
+     we do this right after creating the prev frame, to avoid ever
+     ending up with two frames with the same id in the frame
+     chain.  */
+  if (prev_frame != NULL
+      && frame_id_eq (get_frame_id (prev_frame),
+		      get_frame_id (this_frame)))
+    {
+      if (frame_debug)
+	{
+	  fprintf_unfiltered (gdb_stdlog, "-> ");
+	  fprint_frame (gdb_stdlog, NULL);
+	  fprintf_unfiltered (gdb_stdlog, " // this frame has same ID }\n");
+	}
+      this_frame->stop_reason = UNWIND_SAME_ID;
+      /* Unlink.  */
+      prev_frame->next = NULL;
+      this_frame->prev = NULL;
+      return NULL;
+    }
+
+  return prev_frame;
 }
 
 /* Construct a new "struct frame_info" and link it previous to
Index: gdb-7.6.1/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.S
===================================================================
--- /dev/null
+++ gdb-7.6.1/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.S
@@ -0,0 +1,540 @@
+/*
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+	/* The FDE entry for "stop_frame" in the .debug_frame section has
+	been hand modified to mark a set of registers as DW_CFA_same_value.
+	Otherwise this file is as generated by gcc 4.7.2 for x86_64.  */
+	.file	"dw2-dup-frame.c"
+	.text
+.Ltext0:
+	.globl	stop_frame
+	.type	stop_frame, @function
+stop_frame:
+.LFB0:
+	.file 1 "dw2-dup-frame.c"
+	.loc 1 19 0
+	pushq	%rbp
+.LCFI0:
+	movq	%rsp, %rbp
+.LCFI1:
+	.loc 1 22 0
+	popq	%rbp
+.LCFI2:
+	ret
+.LFE0:
+	.size	stop_frame, .-stop_frame
+	.globl	first_frame
+	.type	first_frame, @function
+first_frame:
+.LFB1:
+	.loc 1 26 0
+	pushq	%rbp
+.LCFI3:
+	movq	%rsp, %rbp
+.LCFI4:
+	.loc 1 27 0
+	movl	$0, %eax
+	call	stop_frame
+	.loc 1 28 0
+	popq	%rbp
+.LCFI5:
+	ret
+.LFE1:
+	.size	first_frame, .-first_frame
+	.globl	main
+	.type	main, @function
+main:
+.LFB2:
+	.loc 1 32 0
+	pushq	%rbp
+.LCFI6:
+	movq	%rsp, %rbp
+.LCFI7:
+	.loc 1 33 0
+	movl	$0, %eax
+	call	first_frame
+	.loc 1 35 0
+	movl	$0, %eax
+	.loc 1 36 0
+	popq	%rbp
+.LCFI8:
+	ret
+.LFE2:
+	.size	main, .-main
+	.section	.debug_frame,"",@progbits
+.Lframe0:
+	.long	.LECIE0-.LSCIE0
+.LSCIE0:
+	.long	0xffffffff
+	.byte	0x1
+	.string	""
+	.uleb128 0x1
+	.sleb128 -8
+	.byte	0x10
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.byte	0x90
+	.uleb128 0x1
+	.align 8
+.LECIE0:
+	/* This FDE entry, for stop_frame was modified to mark
+	   registers 0 -> 16 (rax..ra/rip) as being DW_CFA_same_value.  */
+.LSFDE0:
+	.long	.LEFDE0-.LASFDE0
+.LASFDE0:
+	.long	.Lframe0
+	.quad	.LFB0
+	.quad	.LFE0-.LFB0
+
+		/* START OF NEW CONTENT.  */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x0			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x1			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x2			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x3			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x4			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x5			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x6			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x7			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x8			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x9			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0xa			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0xb			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0xc			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0xd			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0xe			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0xf			/*   ULEB128 register */
+	.byte	0x8			/* DW_CFA_same_value */
+	.uleb128 0x10			 /*   ULEB128 register */
+		/* END OF NEW CONTENT.  */
+
+	.byte	0x4
+	.long	.LCFI0-.LFB0
+	.byte	0xe
+	.uleb128 0x10
+	.byte	0x86
+	.uleb128 0x2
+	.byte	0x4
+	.long	.LCFI1-.LCFI0
+	.byte	0xd
+	.uleb128 0x6
+	.byte	0x4
+	.long	.LCFI2-.LCFI1
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.align 8
+.LEFDE0:
+.LSFDE2:
+	.long	.LEFDE2-.LASFDE2
+.LASFDE2:
+	.long	.Lframe0
+	.quad	.LFB1
+	.quad	.LFE1-.LFB1
+	.byte	0x4
+	.long	.LCFI3-.LFB1
+	.byte	0xe
+	.uleb128 0x10
+	.byte	0x86
+	.uleb128 0x2
+	.byte	0x4
+	.long	.LCFI4-.LCFI3
+	.byte	0xd
+	.uleb128 0x6
+	.byte	0x4
+	.long	.LCFI5-.LCFI4
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.align 8
+.LEFDE2:
+.LSFDE4:
+	.long	.LEFDE4-.LASFDE4
+.LASFDE4:
+	.long	.Lframe0
+	.quad	.LFB2
+	.quad	.LFE2-.LFB2
+	.byte	0x4
+	.long	.LCFI6-.LFB2
+	.byte	0xe
+	.uleb128 0x10
+	.byte	0x86
+	.uleb128 0x2
+	.byte	0x4
+	.long	.LCFI7-.LCFI6
+	.byte	0xd
+	.uleb128 0x6
+	.byte	0x4
+	.long	.LCFI8-.LCFI7
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.align 8
+.LEFDE4:
+	.section	.eh_frame,"a",@progbits
+.Lframe1:
+	.long	.LECIE1-.LSCIE1
+.LSCIE1:
+	.long	0
+	.byte	0x1
+	.string	"zR"
+	.uleb128 0x1
+	.sleb128 -8
+	.byte	0x10
+	.uleb128 0x1
+	.byte	0x3
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.byte	0x90
+	.uleb128 0x1
+	.align 8
+.LECIE1:
+.LSFDE7:
+	.long	.LEFDE7-.LASFDE7
+.LASFDE7:
+	.long	.LASFDE7-.Lframe1
+	.long	.LFB0
+	.long	.LFE0-.LFB0
+	.uleb128 0
+	.byte	0x4
+	.long	.LCFI0-.LFB0
+	.byte	0xe
+	.uleb128 0x10
+	.byte	0x86
+	.uleb128 0x2
+	.byte	0x4
+	.long	.LCFI1-.LCFI0
+	.byte	0xd
+	.uleb128 0x6
+	.byte	0x4
+	.long	.LCFI2-.LCFI1
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.align 8
+.LEFDE7:
+.LSFDE9:
+	.long	.LEFDE9-.LASFDE9
+.LASFDE9:
+	.long	.LASFDE9-.Lframe1
+	.long	.LFB1
+	.long	.LFE1-.LFB1
+	.uleb128 0
+	.byte	0x4
+	.long	.LCFI3-.LFB1
+	.byte	0xe
+	.uleb128 0x10
+	.byte	0x86
+	.uleb128 0x2
+	.byte	0x4
+	.long	.LCFI4-.LCFI3
+	.byte	0xd
+	.uleb128 0x6
+	.byte	0x4
+	.long	.LCFI5-.LCFI4
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.align 8
+.LEFDE9:
+.LSFDE11:
+	.long	.LEFDE11-.LASFDE11
+.LASFDE11:
+	.long	.LASFDE11-.Lframe1
+	.long	.LFB2
+	.long	.LFE2-.LFB2
+	.uleb128 0
+	.byte	0x4
+	.long	.LCFI6-.LFB2
+	.byte	0xe
+	.uleb128 0x10
+	.byte	0x86
+	.uleb128 0x2
+	.byte	0x4
+	.long	.LCFI7-.LCFI6
+	.byte	0xd
+	.uleb128 0x6
+	.byte	0x4
+	.long	.LCFI8-.LCFI7
+	.byte	0xc
+	.uleb128 0x7
+	.uleb128 0x8
+	.align 8
+.LEFDE11:
+	.text
+.Letext0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.long	0x8c
+	.value	0x2
+	.long	.Ldebug_abbrev0
+	.byte	0x8
+	.uleb128 0x1
+	.long	.LASF2
+	.byte	0x1
+	.long	.LASF3
+	.long	.LASF4
+	.quad	.Ltext0
+	.quad	.Letext0
+	.long	.Ldebug_line0
+	.uleb128 0x2
+	.byte	0x1
+	.long	.LASF0
+	.byte	0x1
+	.byte	0x12
+	.quad	.LFB0
+	.quad	.LFE0
+	.long	.LLST0
+	.byte	0x1
+	.uleb128 0x3
+	.byte	0x1
+	.long	.LASF1
+	.byte	0x1
+	.byte	0x19
+	.quad	.LFB1
+	.quad	.LFE1
+	.long	.LLST1
+	.byte	0x1
+	.uleb128 0x4
+	.byte	0x1
+	.long	.LASF5
+	.byte	0x1
+	.byte	0x1f
+	.long	0x88
+	.quad	.LFB2
+	.quad	.LFE2
+	.long	.LLST2
+	.byte	0x1
+	.uleb128 0x5
+	.byte	0x4
+	.byte	0x5
+	.string	"int"
+	.byte	0
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1
+	.uleb128 0x11
+	.byte	0x1
+	.uleb128 0x25
+	.uleb128 0xe
+	.uleb128 0x13
+	.uleb128 0xb
+	.uleb128 0x3
+	.uleb128 0xe
+	.uleb128 0x1b
+	.uleb128 0xe
+	.uleb128 0x11
+	.uleb128 0x1
+	.uleb128 0x12
+	.uleb128 0x1
+	.uleb128 0x10
+	.uleb128 0x6
+	.byte	0
+	.byte	0
+	.uleb128 0x2
+	.uleb128 0x2e
+	.byte	0
+	.uleb128 0x3f
+	.uleb128 0xc
+	.uleb128 0x3
+	.uleb128 0xe
+	.uleb128 0x3a
+	.uleb128 0xb
+	.uleb128 0x3b
+	.uleb128 0xb
+	.uleb128 0x11
+	.uleb128 0x1
+	.uleb128 0x12
+	.uleb128 0x1
+	.uleb128 0x40
+	.uleb128 0x6
+	.uleb128 0x2117
+	.uleb128 0xc
+	.byte	0
+	.byte	0
+	.uleb128 0x3
+	.uleb128 0x2e
+	.byte	0
+	.uleb128 0x3f
+	.uleb128 0xc
+	.uleb128 0x3
+	.uleb128 0xe
+	.uleb128 0x3a
+	.uleb128 0xb
+	.uleb128 0x3b
+	.uleb128 0xb
+	.uleb128 0x11
+	.uleb128 0x1
+	.uleb128 0x12
+	.uleb128 0x1
+	.uleb128 0x40
+	.uleb128 0x6
+	.uleb128 0x2116
+	.uleb128 0xc
+	.byte	0
+	.byte	0
+	.uleb128 0x4
+	.uleb128 0x2e
+	.byte	0
+	.uleb128 0x3f
+	.uleb128 0xc
+	.uleb128 0x3
+	.uleb128 0xe
+	.uleb128 0x3a
+	.uleb128 0xb
+	.uleb128 0x3b
+	.uleb128 0xb
+	.uleb128 0x49
+	.uleb128 0x13
+	.uleb128 0x11
+	.uleb128 0x1
+	.uleb128 0x12
+	.uleb128 0x1
+	.uleb128 0x40
+	.uleb128 0x6
+	.uleb128 0x2116
+	.uleb128 0xc
+	.byte	0
+	.byte	0
+	.uleb128 0x5
+	.uleb128 0x24
+	.byte	0
+	.uleb128 0xb
+	.uleb128 0xb
+	.uleb128 0x3e
+	.uleb128 0xb
+	.uleb128 0x3
+	.uleb128 0x8
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_loc,"",@progbits
+.Ldebug_loc0:
+.LLST0:
+	.quad	.LFB0-.Ltext0
+	.quad	.LCFI0-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 8
+	.quad	.LCFI0-.Ltext0
+	.quad	.LCFI1-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 16
+	.quad	.LCFI1-.Ltext0
+	.quad	.LCFI2-.Ltext0
+	.value	0x2
+	.byte	0x76
+	.sleb128 16
+	.quad	.LCFI2-.Ltext0
+	.quad	.LFE0-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 8
+	.quad	0
+	.quad	0
+.LLST1:
+	.quad	.LFB1-.Ltext0
+	.quad	.LCFI3-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 8
+	.quad	.LCFI3-.Ltext0
+	.quad	.LCFI4-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 16
+	.quad	.LCFI4-.Ltext0
+	.quad	.LCFI5-.Ltext0
+	.value	0x2
+	.byte	0x76
+	.sleb128 16
+	.quad	.LCFI5-.Ltext0
+	.quad	.LFE1-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 8
+	.quad	0
+	.quad	0
+.LLST2:
+	.quad	.LFB2-.Ltext0
+	.quad	.LCFI6-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 8
+	.quad	.LCFI6-.Ltext0
+	.quad	.LCFI7-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 16
+	.quad	.LCFI7-.Ltext0
+	.quad	.LCFI8-.Ltext0
+	.value	0x2
+	.byte	0x76
+	.sleb128 16
+	.quad	.LCFI8-.Ltext0
+	.quad	.LFE2-.Ltext0
+	.value	0x2
+	.byte	0x77
+	.sleb128 8
+	.quad	0
+	.quad	0
+	.section	.debug_aranges,"",@progbits
+	.long	0x2c
+	.value	0x2
+	.long	.Ldebug_info0
+	.byte	0x8
+	.byte	0
+	.value	0
+	.value	0
+	.quad	.Ltext0
+	.quad	.Letext0-.Ltext0
+	.quad	0
+	.quad	0
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",@progbits,1
+.LASF0:
+	.string	"stop_frame"
+.LASF3:
+	.string	"dw2-reg-undefined.c"
+.LASF2:
+	.string	"GNU C 4.7.2"
+.LASF1:
+	.string	"first_frame"
+.LASF5:
+	.string	"main"
+.LASF4:
+	.string	"/home/username/src/gdb/testsuite/gdb.dwarf2"
+	.ident	"GCC: (GNU) 4.7.2"
+	.section	.note.GNU-stack,"",@progbits
Index: gdb-7.6.1/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.c
===================================================================
--- /dev/null
+++ gdb-7.6.1/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.c
@@ -0,0 +1,36 @@
+/*
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+void
+stop_frame ()
+{
+  /* The debug information for this frame is modified in the accompanying
+     .S file, to mark a set of registers as being DW_CFA_same_value.  */
+}
+
+void
+first_frame ()
+{
+  stop_frame ();
+}
+
+int
+main ()
+{
+  first_frame ();
+
+  return 0;
+}
Index: gdb-7.6.1/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.exp
===================================================================
--- /dev/null
+++ gdb-7.6.1/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.exp
@@ -0,0 +1,44 @@
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# This test can only be run on x86_64 targets.
+if {![istarget "x86_64-*-*"] || ![is_lp64_target]} {
+    return 0
+}
+
+standard_testfile .S
+
+if { [prepare_for_testing $testfile.exp $testfile $srcfile {nodebug}] } {
+    return -1
+}
+
+if ![runto stop_frame] {
+    perror "Failed to stop in stop_frame"
+    return -1
+}
+
+gdb_test "bt" \
+    "#0  stop_frame \[^\r\n\]*\r\nBacktrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)" \
+    "backtrace from stop_frame"
+
+gdb_test "up" \
+    "Initial frame selected; you cannot go up\\\." \
+    "up from stop_frame"