From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 12019 invoked by alias); 20 Feb 2006 22:03:36 -0000 Received: (qmail 12008 invoked by uid 22791); 20 Feb 2006 22:03:34 -0000 X-Spam-Check-By: sourceware.org Received: from nevyn.them.org (HELO nevyn.them.org) (66.93.172.17) by sourceware.org (qpsmtpd/0.31.1) with ESMTP; Mon, 20 Feb 2006 22:03:33 +0000 Received: from drow by nevyn.them.org with local (Exim 4.54) id 1FBJ7z-0007i4-4Y; Mon, 20 Feb 2006 17:03:31 -0500 Date: Tue, 21 Feb 2006 04:33:00 -0000 From: Daniel Jacobowitz To: Shaun Jackman , gdb-patches@sourceware.org Subject: Fix a crash when stepping and unwinding fails Message-ID: <20060220220331.GA29363@nevyn.them.org> Mail-Followup-To: Shaun Jackman , gdb-patches@sourceware.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.8i X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2006-02/txt/msg00388.txt.bz2 This patch stops GDB from segfaulting when we step into a function, but can not unwind back out of the function. We would previously call get_prev_frame, which would return NULL, and then try to get_frame_pc (NULL). Now we'll issue this error instead, and stop stepping: Could not step out of the function at 0x80144400 - unwinding failed It's still not great, but at least it's an improvement over crashing. It is reasonably likely that we've just stepped over a standard function call, and that consequentially the function return address is in the standard place for the architecture; in fact, GDB used to have a hook for this, before the frame overhaul: SAVED_PC_AFTER_CALL. But it's gone now and there's no easy analogue, and it was never 100% reliable anyway. So unfortunately, if we single-step out to an address that we can't find a way to unwind from, we'll stop instead of stepping out. Hmm. Alternatively, we could stop stepping without an error. Would that be better? Seems likely. I'll wait for comments before I try implementing that, though. Should we warn when we do that, in addition to stopping, or is the warning just noise? Shaun, I believe this is the crash you reported on gdb@ several times. I can't think of any easy way to write a test for this. -- Daniel Jacobowitz CodeSourcery 2006-02-20 Daniel Jacobowitz * infrun.c (insert_step_resume_breakpoint_at_frame): Add USE_PREVIOUS argument. Issue an error if we can not unwind to the previous frame. (handle_inferior_event): Update calls. Index: src/gdb/infrun.c =================================================================== --- src.orig/gdb/infrun.c 2006-01-04 14:34:58.000000000 -0500 +++ src/gdb/infrun.c 2006-02-20 15:59:18.000000000 -0500 @@ -942,7 +942,7 @@ void init_execution_control_state (struc void handle_inferior_event (struct execution_control_state *ecs); static void step_into_function (struct execution_control_state *ecs); -static void insert_step_resume_breakpoint_at_frame (struct frame_info *step_frame); +static void insert_step_resume_breakpoint_at_frame (struct frame_info *, int); static void insert_step_resume_breakpoint_at_sal (struct symtab_and_line sr_sal, struct frame_id sr_id); static void stop_stepping (struct execution_control_state *ecs); @@ -1965,7 +1965,7 @@ process_event_stop_test: code paths as single-step - set a breakpoint at the signal return address and then, once hit, step off that breakpoint. */ - insert_step_resume_breakpoint_at_frame (get_current_frame ()); + insert_step_resume_breakpoint_at_frame (get_current_frame (), 0); ecs->step_after_step_resume_breakpoint = 1; keep_going (ecs); return; @@ -1987,7 +1987,7 @@ process_event_stop_test: Note that this is only needed for a signal delivered while in the single-step range. Nested signals aren't a problem as they eventually all return. */ - insert_step_resume_breakpoint_at_frame (get_current_frame ()); + insert_step_resume_breakpoint_at_frame (get_current_frame (), 0); keep_going (ecs); return; } @@ -2396,7 +2396,7 @@ process_event_stop_test: /* We're doing a "next", set a breakpoint at callee's return address (the address at which the caller will resume). */ - insert_step_resume_breakpoint_at_frame (get_prev_frame (get_current_frame ())); + insert_step_resume_breakpoint_at_frame (get_current_frame (), 1); keep_going (ecs); return; } @@ -2459,7 +2459,7 @@ process_event_stop_test: /* Set a breakpoint at callee's return address (the address at which the caller will resume). */ - insert_step_resume_breakpoint_at_frame (get_prev_frame (get_current_frame ())); + insert_step_resume_breakpoint_at_frame (get_current_frame (), 1); keep_going (ecs); return; } @@ -2528,7 +2528,7 @@ process_event_stop_test: { /* Set a breakpoint at callee's return address (the address at which the caller will resume). */ - insert_step_resume_breakpoint_at_frame (get_prev_frame (get_current_frame ())); + insert_step_resume_breakpoint_at_frame (get_current_frame (), 1); keep_going (ecs); return; } @@ -2741,22 +2741,33 @@ insert_step_resume_breakpoint_at_sal (st that the function/signal handler being skipped eventually returns to the breakpoint inserted at RETURN_FRAME.pc. - For the skip-function case, the function may have been reached by - either single stepping a call / return / signal-return instruction, - or by hitting a breakpoint. In all cases, the RETURN_FRAME belongs - to the skip-function's caller. + If USE_PREVIOUS is zero, RETURN_FRAME belongs to the function being + skipped. The function may have been reached by either single + stepping a call / return / signal-return instruction, or by hitting + a breakpoint. For the signals case, this is called with the interrupted function's frame. The signal handler, when it returns, will resume the interrupted function at RETURN_FRAME.pc. */ static void -insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame) +insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame, + int use_previous) { struct symtab_and_line sr_sal; init_sal (&sr_sal); /* initialize to zeros */ + if (use_previous) + { + struct frame_info *caller_frame; + caller_frame = get_prev_frame (return_frame); + if (caller_frame == NULL) + error (_("Could not step out of the function at 0x%x - unwinding failed"), + get_frame_pc (return_frame)); + return_frame = caller_frame; + } + sr_sal.pc = ADDR_BITS_REMOVE (get_frame_pc (return_frame)); sr_sal.section = find_pc_overlay (sr_sal.pc);