old-cross-binutils/gdb/testsuite/gdb.dwarf2/dw2-dup-frame.S

541 lines
9.1 KiB
ArmAsm
Raw Normal View History

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.
2013-11-22 11:51:59 +00:00
/*
Copyright 2013-2015 Free Software Foundation, Inc.
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.
2013-11-22 11:51:59 +00:00
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