diff options
author | Pedro Alves <palves@redhat.com> | 2013-11-22 11:51:59 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2013-11-22 13:41:43 +0000 |
commit | be2c48b4d50b992ba83bc51f086e316621a03a14 (patch) | |
tree | efaa53faf061be67e4ea1946c1a2e174cbaa4ac1 /gdb/testsuite/gdb.dwarf2 | |
parent | 5ed365b417ae675db9bd42c6920de83027edcc0c (diff) | |
download | gdb-be2c48b4d50b992ba83bc51f086e316621a03a14.zip gdb-be2c48b4d50b992ba83bc51f086e316621a03a14.tar.gz gdb-be2c48b4d50b992ba83bc51f086e316621a03a14.tar.bz2 |
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.
Diffstat (limited to 'gdb/testsuite/gdb.dwarf2')
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-dup-frame.S | 540 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-dup-frame.c | 36 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-dup-frame.exp | 44 |
3 files changed, 620 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.S b/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.S new file mode 100644 index 0000000..4c3bd76 --- /dev/null +++ b/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 diff --git a/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.c b/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.c new file mode 100644 index 0000000..4868d05 --- /dev/null +++ b/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; +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.exp b/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.exp new file mode 100644 index 0000000..a57ab8a --- /dev/null +++ b/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" |