Re: longjmp handling vs. glibc LD_POINTER_GUARD problems

Pedro Alves
2008-05-17T02:17:50Z

[-- Attachment #1: Type: text/plain, Size: 4749 bytes --]

A Wednesday 14 May 2008 19:16:23, Pedro Alves wrote:
> A Wednesday 14 May 2008 19:00:18, Ulrich Weigand wrote:
> > I'm now wondering how we should handle this. Should be
> > implement an ad-hoc solution to retrieve the guard, which
> > may break in the future if glibc changes? Should we require
> > use of LD_POINTER_GUARD=0 (which switches off the pointer
> > guard mechanism) to enable debugging? Am I overlooking some
> > defined interface to get at the value?
>
> No, you're not. There is none. And still
> LD_POINTER_GUARD=0 doesn't help when attaching to an already
> running target.
>

> > Why are we using the get_longjmp_target mechanism instead of
> > just stepping through longjmp until we see where we come out?
>
> You tell me. :-) I had assumed there was a reason. Perhaps
> to support longjumping to a different stack, but that's hardly
> a portable and frequent use case. This seems to be the path
> to go.

Alright, here is a quick hack at it. If this is the path to go,
we can remove a bunch of gdbarch_get_longjmp_target implementations,
and the code around longjmp_resume breakpoints.

The patch implements the "keep stepping if going through a longjmp",
and also, if landing somewhere inner than the current step-resume, we
keep stepping, which is an alternative and simple implementation
of what I proposed here:

http://sourceware.org/ml/gdb-patches/2008-04/msg00162.html

Tested on x86_64-unknown-linux-gnu. The longjmp.exp passes all ok,
and we become immune to pointer mangling.

I've also simplified a bit the test, removing stuff that wasn't
adding any value.

One thing, why do I need to call get_frame_type before calling
frame_unwind_id (first thing after frame cache invalidation)? If I
don't do that, I hit this gdb_assert:

static void
frame_cleanup_after_sniffer (void *arg)
{
...
/* No sniffer should extend the frame chain; sniff based on what is
already certain. */
gdb_assert (!frame->prev_p);

#0 internal_error (file=0x7558a1 "../../src/gdb/frame.c", line=1808,
string=0x755946 "%s: Assertion `%s' failed.")
at ../../src/gdb/utils.c:779
#1 0x00000000005b5b6a in frame_cleanup_after_sniffer (arg=0xafbd40)
at ../../src/gdb/frame.c:1808
#2 0x00000000004570ed in do_my_cleanups (pmy_chain=0xa9b900,
old_chain=0xb13d10) at ../../src/gdb/utils.c:320
#3 0x000000000045709d in do_cleanups (old_chain=0xb13d10)
at ../../src/gdb/utils.c:303
#4 0x00000000005b62d3 in frame_unwind_find_by_frame (this_frame=0xafbd40,
this_cache=0xafbd48)
at ../../src/gdb/frame-unwind.c:105
#5 0x00000000005b2e7f in get_frame_id (fi=0xafbd40)
at ../../src/gdb/frame.c:258
#6 0x00000000005b4c6a in get_prev_frame_1 (this_frame=0xafbd40)
at ../../src/gdb/frame.c:1188
#7 0x00000000005b2f72 in frame_unwind_id (next_frame=0xafbd40)
at ../../src/gdb/frame.c:279
#8 0x0000000000503b00 in handle_inferior_event (ecs=0x7fffd6bca510)
at ../../src/gdb/infrun.c:2605
#9 0x00000000005016cb in wait_for_inferior (treat_exec_as_sigtrap=0)
at ../../src/gdb/infrun.c:1482
#10 0x000000000050142d in proceed (addr=18446744073709551615,
siggnal=TARGET_SIGNAL_DEFAULT, step=1)
at ../../src/gdb/infrun.c:1273
#11 0x00000000004fd64f in step_1 (skip_subroutines=1, single_inst=0,
count_string=0x0) at ../../src/gdb/infcmd.c:784
#12 0x00000000004fd32e in next_command (count_string=0x0, from_tty=1)
at ../../src/gdb/infcmd.c:678
...

The first call to frame_unwind_id a bit below happens to have a get_frame_type
call before, by chance,

if (step_range_end != 1
&& (step_over_calls == STEP_OVER_UNDEBUGGABLE
|| step_over_calls == STEP_OVER_ALL)
>>> && get_frame_type (get_current_frame ()) == SIGTRAMP_FRAME)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into signal trampoline\n");
/* The inferior, while doing a "step" or "next", has ended up in
a signal trampoline (either by a signal being delivered or by
the signal handler returning). Just single-step until the
inferior leaves the trampoline (either by calling the handler
or returning). */
keep_going (ecs);
return;
}

/* Check for subroutine calls. The check for the current frame
equalling the step ID is not necessary - the check of the
previous frame's ID is sufficient - but it is a common case and
cheaper than checking the previous frame's ID.

NOTE: frame_id_eq will never report two invalid frame IDs as
being equal, so to get into this block, both the current and
previous frame must have valid frame IDs. */
if (!frame_id_eq (get_frame_id (get_current_frame ()), step_frame_id)
>>> && frame_id_eq (frame_unwind_id (get_current_frame ()), step_frame_id))
{

--
Pedro Alves

[-- Attachment #2: longjmp.diff --]
[-- Type: text/x-diff, Size: 9914 bytes --]

2008-05-17 Pedro Alves <pedro@codesourcery.com>

* infrun.c (struct execution_control_state): Add
stepping_through_longjmp and longjmp_caller_frame.
(init_execution_control_state): Clear them.
(context_switch): Context switch them.
(handle_inferior_event): If stepping through longjmp, a SIGTRAP is
not random. When a longjmp breakpoint is hit, prepare to step
until the other end. Keep stepping while in an inner frame
relative to the longjmp caller. When coming out on the other end,
check if the step-resume breakpoint is no longer needed.
(currently_stepping): Return true if stepping through longjmp.

* gdbthread.h (struct thread_info): Add stepping_through_longjmp
and longjmp_caller_frame.
(save_infrun_state, load_infrun_state): Context switch them.
* thread.c (save_infrun_state, load_infrun_state): Ditto.

---
gdb/gdbthread.h | 14 +++++++-
gdb/infrun.c | 95 +++++++++++++++++++++++++++++++++++++++++---------------
gdb/thread.c | 12 +++++--
3 files changed, 93 insertions(+), 28 deletions(-)

Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c 2008-05-17 02:38:39.000000000 +0100
+++ src/gdb/infrun.c 2008-05-17 02:49:21.000000000 +0100
@@ -1386,6 +1386,13 @@ struct execution_control_state
int step_after_step_resume_breakpoint;
int stepping_through_solib_after_catch;
bpstat stepping_through_solib_catchpoints;
+
+ /* True if a longjmp call was detected while stepping, and we're
+ single-stepping until the other end. */
+ int stepping_through_longjmp;
+ /* The frame that called longjmp. */
+ struct frame_id longjmp_caller_frame;
+
int new_thread_event;
struct target_waitstatus tmpstatus;
enum infwait_states infwait_state;
@@ -1556,6 +1563,7 @@ init_execution_control_state (struct exe
ecs->infwait_state = infwait_normal_state;
ecs->waiton_ptid = pid_to_ptid (-1);
ecs->wp = &(ecs->ws);
+ ecs->stepping_through_longjmp = 0;
}

/* Return the cached copy of the last pid/waitstatus returned by
@@ -1605,7 +1613,9 @@ context_switch (struct execution_control
ecs->stepping_over_breakpoint,
ecs->stepping_through_solib_after_catch,
ecs->stepping_through_solib_catchpoints,
- ecs->current_line, ecs->current_symtab);
+ ecs->current_line, ecs->current_symtab,
+ ecs->stepping_through_longjmp,
+ ecs->longjmp_caller_frame);

/* Load infrun state for the new thread. */
load_infrun_state (ecs->ptid, &prev_pc,
@@ -1615,7 +1625,9 @@ context_switch (struct execution_control
&ecs->stepping_over_breakpoint,
&ecs->stepping_through_solib_after_catch,
&ecs->stepping_through_solib_catchpoints,
- &ecs->current_line, &ecs->current_symtab);
+ &ecs->current_line, &ecs->current_symtab,
+ &ecs->stepping_through_longjmp,
+ &ecs->longjmp_caller_frame);
}

switch_to_thread (ecs->ptid);
@@ -2449,7 +2461,8 @@ handle_inferior_event (struct execution_
ecs->random_signal
= !(bpstat_explains_signal (stop_bpstat)
|| stepping_over_breakpoint
- || (step_range_end && step_resume_breakpoint == NULL));
+ || (step_range_end && step_resume_breakpoint == NULL)
+ || ecs->stepping_through_longjmp);
else
{
ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
@@ -2575,35 +2588,24 @@ process_event_stop_test:
switch (what.main_action)
{
case BPSTAT_WHAT_SET_LONGJMP_RESUME:
- /* If we hit the breakpoint at longjmp while stepping, we
- install a momentary breakpoint at the target of the
- jmp_buf. */
-
+ /* If we hit the breakpoint at longjmp while stepping, prepare
+ to step all the way through it. */
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n");

+ /* Step over this longjmp breakpoint. */
ecs->stepping_over_breakpoint = 1;

- if (!gdbarch_get_longjmp_target_p (current_gdbarch)
- || !gdbarch_get_longjmp_target (current_gdbarch,
- get_current_frame (), &jmp_buf_pc))
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "\
-infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
- keep_going (ecs);
- return;
- }
-
- /* We're going to replace the current step-resume breakpoint
- with a longjmp-resume breakpoint. */
- if (step_resume_breakpoint != NULL)
- delete_step_resume_breakpoint (&step_resume_breakpoint);
+ /* Why do I need to call this? */
+ get_frame_type (get_current_frame ());

- /* Insert a breakpoint at resume address. */
- insert_longjmp_resume_breakpoint (jmp_buf_pc);
+ /* Store the frame of the caller. This is used to compare
+ with any step-resume step set. */
+ ecs->longjmp_caller_frame = frame_unwind_id (get_current_frame ());

+ /* See you on the other side! */
+ ecs->stepping_through_longjmp = 1;
keep_going (ecs);
return;

@@ -2819,6 +2821,50 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
return;
}

+ if (ecs->stepping_through_longjmp)
+ {
+ struct frame_info *frame = get_current_frame ();
+ struct frame_id current_frame = get_frame_id (frame);
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping through longjmp\n");
+ if (frame_id_inner (get_frame_arch (frame),
+ current_frame, ecs->longjmp_caller_frame))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: still in longjmp\n");
+ keep_going (ecs);
+ return;
+ }
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: got out of longjmp\n");
+ ecs->stepping_through_longjmp = 0;
+
+ /* Now decide if we should proceed to the step-resume
+ breakpoint, or if we already went passed it. */
+
+ if (step_resume_breakpoint)
+ {
+ if (frame_id_inner (get_frame_arch (frame),
+ current_frame, step_resume_breakpoint->frame_id))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: longjmp-resume is inner than step-resume\n");
+ }
+ else
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: step-resume overran by longjmp\n");
+ delete_step_resume_breakpoint (&step_resume_breakpoint);
+ }
+ }
+ }
+
if (step_resume_breakpoint)
{
if (debug_infrun)
@@ -3183,6 +3229,7 @@ currently_stepping (struct execution_con
{
return (((step_range_end && step_resume_breakpoint == NULL)
|| stepping_over_breakpoint)
+ || ecs->stepping_through_longjmp
|| ecs->stepping_through_solib_after_catch
|| bpstat_should_step ());
}
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h 2008-05-17 02:39:51.000000000 +0100
+++ src/gdb/gdbthread.h 2008-05-17 02:45:44.000000000 +0100
@@ -63,6 +63,12 @@ struct thread_info
when we finally do stop stepping. */
bpstat stepping_through_solib_catchpoints;

+ /* True if a longjmp call was detected while stepping, and we're
+ single-stepping until the other end. */
+ int stepping_through_longjmp;
+ /* The frame that called longjmp. */
+ struct frame_id longjmp_caller_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
};
@@ -126,7 +132,9 @@ extern void save_infrun_state (ptid_t pt
int stepping_through_solib_after_catch,
bpstat stepping_through_solib_catchpoints,
int current_line,
- struct symtab *current_symtab);
+ struct symtab *current_symtab,
+ int stepping_through_longjmp,
+ struct frame_id longjmp_caller_frame);

/* infrun context switch: load the debugger state previously saved
for the given thread. */
@@ -141,7 +149,9 @@ extern void load_infrun_state (ptid_t pt
int *stepping_through_solib_affter_catch,
bpstat *stepping_through_solib_catchpoints,
int *current_line,
- struct symtab **current_symtab);
+ struct symtab **current_symtab,
+ int *stepping_through_longjmp,
+ struct frame_id *longjmp_caller_frame);

/* Switch from one thread to another. */
extern void switch_to_thread (ptid_t ptid);
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c 2008-05-17 02:39:54.000000000 +0100
+++ src/gdb/thread.c 2008-05-17 02:47:18.000000000 +0100
@@ -323,7 +323,9 @@ load_infrun_state (ptid_t ptid,
int *stepping_through_solib_after_catch,
bpstat *stepping_through_solib_catchpoints,
int *current_line,
- struct symtab **current_symtab)
+ struct symtab **current_symtab,
+ int *stepping_through_longjmp,
+ struct frame_id *longjmp_caller_frame)
{
struct thread_info *tp;

@@ -346,6 +348,8 @@ load_infrun_state (ptid_t ptid,
tp->stepping_through_solib_catchpoints;
*current_line = tp->current_line;
*current_symtab = tp->current_symtab;
+ *stepping_through_longjmp = tp->stepping_through_longjmp;
+ *longjmp_caller_frame = tp->longjmp_caller_frame;
}

/* Save infrun state for the thread PID. */
@@ -362,7 +366,9 @@ save_infrun_state (ptid_t ptid,
int stepping_through_solib_after_catch,
bpstat stepping_through_solib_catchpoints,
int current_line,
- struct symtab *current_symtab)
+ struct symtab *current_symtab,
+ int stepping_through_longjmp,
+ struct frame_id longjmp_caller_frame)
{
struct thread_info *tp;

@@ -383,6 +389,8 @@ save_infrun_state (ptid_t ptid,
tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints;
tp->current_line = current_line;
tp->current_symtab = current_symtab;
+ tp->stepping_through_longjmp = stepping_through_longjmp;
+ tp->longjmp_caller_frame = longjmp_caller_frame;
}

/* Return true if TP is an active thread. */

[-- Attachment #3: longjmp_tests_outer.diff --]
[-- Type: text/x-diff, Size: 7818 bytes --]

2008-05-17 Pedro Alves <pedro@codesourcery.com>

* longjmp.c, longjmp.exp: Add tests to test ignoring inner
longjmp resumes while stepping, and update current tests.

---
gdb/testsuite/gdb.base/longjmp.c | 103 ++++++++++++++++++++++++++++++++-----
gdb/testsuite/gdb.base/longjmp.exp | 96 ++++++++++++++++++----------------
2 files changed, 142 insertions(+), 57 deletions(-)

Index: src/gdb/testsuite/gdb.base/longjmp.c
===================================================================
--- src.orig/gdb/testsuite/gdb.base/longjmp.c 2008-05-04 20:43:31.000000000 +0100
+++ src/gdb/testsuite/gdb.base/longjmp.c 2008-05-17 02:36:56.000000000 +0100
@@ -19,6 +19,7 @@
#include <setjmp.h>

jmp_buf env;
+jmp_buf env2;

volatile int longjmps = 0;
volatile int resumes = 0;
@@ -33,7 +34,7 @@ call_longjmp (jmp_buf *buf)
void
hidden_longjmp (void)
{
- if (setjmp (env) == 0) /* longjmp caught */
+ if (setjmp (env) == 0)
{
call_longjmp (&env);
}
@@ -41,41 +42,117 @@ hidden_longjmp (void)
resumes++;
}

+void
+hidden_longjmp_2 (void)
+{
+ if (setjmp (env) == 0)
+ {
+ if (setjmp (env2) == 0)
+ {
+ longjmps++;
+ longjmp (env2, 1);
+ }
+ else
+ {
+ resumes++;
+ longjmps++;
+ longjmp (env, 1);
+ }
+ }
+ else
+ {
+ resumes++;
+ }
+}
+
+void
+hidden_longjmp_3_1 (void)
+{
+ if (setjmp (env2) == 0)
+ {
+ longjmps++;
+ longjmp (env2, 1);
+ }
+ else
+ {
+ resumes++;
+ longjmps++;
+ longjmp (env, 1);
+ }
+}
+
+void
+hidden_longjmp_3 (void)
+{
+ hidden_longjmp_3_1 ();
+}
+
int
main ()
{
volatile int i = 0;

/* Pattern 1 - simple longjmp. */
- if (setjmp (env) == 0) /* patt1 */
+ if (setjmp (env) == 0)
{
longjmps++;
- longjmp (env, 1);
+ longjmp (env, 1); /* patt1 */
}
else
{
- resumes++;
+ resumes++; /* resume1 */
}

- i = 1; /* miss_step_1 */
-

/* Pattern 2 - longjmp from an inner function. */
- if (setjmp (env) == 0) /* patt2 */
+ if (setjmp (env) == 0)
{
- call_longjmp (&env);
+ call_longjmp (&env); /* patt2 */
}
else
{
- resumes++;
+ resumes++; /* resume2 */
}

- i = 2; /* miss_step_2 */

- /* Pattern 3 - setjmp/longjmp inside stepped-over function. */
- hidden_longjmp (); /* patt3 */
+ /* Pattern 3 - prefer longjmp resume.
+
+ This tests if GDB chooses the longjmp-resume over the step-resume
+ breakpoint. If GDB chooses wrongly, a step over the
+ hidden_longjmp_3 function will resume the inferior and pass
+ straight the else clause without stopping to step. */
+ if (setjmp (env) == 0)
+ hidden_longjmp_3 (); /* patt3 */
+ else
+ resumes++; /* resume3 */
+
+
+ /* Pattern 4 - prefer longjmp resume after step.
+
+ Quite similar, but in this case, we step into hidden_longjmp_3
+ before next'ing over the longjmp. In this case, the step-resume
+ breakpoint will be set in an inner stack. Check if GDB still
+ detects that the longjmp-resume address is prefered. */
+ if (setjmp (env) == 0)
+ hidden_longjmp_3 (); /* patt4 */
+ else
+ resumes++; /* resume4 */
+
+
+ /* Pattern 5 - setjmp/longjmp handled inside stepped-over function.
+
+ Test that we don't miss-handle internal setjmp/longjmp sequences.
+ A next over this should not stop at the longjmp resume
+ address. */
+ hidden_longjmp (); /* patt5 */
+ i = 5; /* patt_end5. */
+
+
+ /* Pattern 6 - nested setjmp/longjmp handled inside stepped-over
+ function. */
+ hidden_longjmp_2 (); /* patt6 */
+ i = 6; /* patt_end6. */

- i = 3; /* patt_end3. */

return 0;
}
Index: src/gdb/testsuite/gdb.base/longjmp.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/longjmp.exp 2008-05-04 20:43:31.000000000 +0100
+++ src/gdb/testsuite/gdb.base/longjmp.exp 2008-05-17 02:30:18.000000000 +0100
@@ -47,12 +47,12 @@ if ![runto_main] then {
return 0
}

-set bp_miss_step_1 [gdb_get_line_number "miss_step_1"]
-set bp_miss_step_2 [gdb_get_line_number "miss_step_2"]
-
set bp_start_test_1 [gdb_get_line_number "patt1"]
set bp_start_test_2 [gdb_get_line_number "patt2"]
set bp_start_test_3 [gdb_get_line_number "patt3"]
+set bp_start_test_4 [gdb_get_line_number "patt4"]
+set bp_start_test_5 [gdb_get_line_number "patt5"]
+set bp_start_test_6 [gdb_get_line_number "patt6"]

#
# Pattern 1 - simple longjmp.
@@ -65,25 +65,7 @@ gdb_test "break $bp_start_test_1" \
"breakpoint at pattern 1 start"
gdb_test "continue" "patt1.*" "continue to breakpoint at pattern 1 start"

-# set safe-net break
-gdb_test "break $bp_miss_step_1" \
- "Breakpoint.*at.* file .*$srcfile, line.*$bp_miss_step_1.*" \
- "breakpoint at miss_step_1"
-
-gdb_test "next" "longjmps\\+\\+;.*" "next over setjmp (1)"
-gdb_test "next" "longjmp \\(env, 1\\);.*" "next to longjmp (1)"
-
-set msg "next over longjmp(1)"
-gdb_test_multiple "next" $msg {
- -re ".*patt1.*" {
- pass $msg
- gdb_test "next" "resumes\\+\\+.*" "next into else block (1)"
- gdb_test "next" "miss_step_1.*" "next into safety net (1)"
- }
- -re "miss_step_1.*" {
- fail $msg
- }
-}
+gdb_test "next" ".*resume1.*" "next over longjmp(1)"

#
# Pattern 2 - longjmp from an inner function.
@@ -96,28 +78,10 @@ gdb_test "break $bp_start_test_2" \
"breakpoint at pattern 2 start"
gdb_test "continue" "patt2.*" "continue to breakpoint at pattern 2 start"

-# set safe-net break
-gdb_test "break $bp_miss_step_2" \
- "Breakpoint.*at.* file .*$srcfile, line.*$bp_miss_step_2.*" \
- "breakpoint at miss_step_2"
-
-gdb_test "next" "call_longjmp.*" "next over setjmp (2)"
-
-set msg "next over call_longjmp (2)"
-gdb_test_multiple "next" $msg {
- -re ".*patt2.*" {
- pass $msg
-
- gdb_test "next" "resumes\\+\\+.*" "next into else block (2)"
- gdb_test "next" "miss_step_2.*" "next into safety net (2)"
- }
- -re "miss_step_2.*" {
- fail $msg
- }
-}
+gdb_test "next" ".*resume2.*" "next over call_longjmp (2)"

#
-# Pattern 3 - setjmp/longjmp inside stepped-over function.
+# Pattern 3 - prefer longjmp resume
#

delete_breakpoints
@@ -125,6 +89,50 @@ delete_breakpoints
gdb_test "break $bp_start_test_3" \
"Breakpoint.*at.* file .*$srcfile, line.*$bp_start_test_3.*" \
"breakpoint at pattern 3 start"
-gdb_test "continue" "patt3.*" "continue to breakpoint at pattern 3 start"
+gdb_test "continue" "patt3.*" \
+ "continue to breakpoint at pattern 3 start"
+
+gdb_test "next" "resume3.*" "next over hidden_longjmp_3 (3)"
+
+
+#
+# Pattern 4 - prefer longjmp resume after step
+#
+
+delete_breakpoints
+
+gdb_test "break $bp_start_test_4" \
+ "Breakpoint.*at.* file .*$srcfile, line.*$bp_start_test_4.*" \
+ "breakpoint at pattern 4 start"
+gdb_test "continue" "patt4.*" "continue to breakpoint at pattern 4 start"
+
+gdb_test "step" "hidden_longjmp_3_1 \\(\\).*" "step into hidden_longjmp_3 (4)"
+
+gdb_test "next" "resume4.*" "next over hidden_longjmp_3_1 (4)"
+
+#
+# Pattern 5 - setjmp/longjmp handled inside stepped-over function.
+#
+
+delete_breakpoints
+
+gdb_test "break $bp_start_test_5" \
+ "Breakpoint.*at.* file .*$srcfile, line.*$bp_start_test_5.*" \
+ "breakpoint at pattern 5 start"
+gdb_test "continue" "patt5.*" "continue to breakpoint at pattern 5 start"
+
+gdb_test "next" "patt_end5.*" "next over patt5"
+
+#
+# Pattern 6 - nested setjmp/longjmp handled inside stepped-over
+# function.
+#
+
+delete_breakpoints
+
+gdb_test "break $bp_start_test_6" \
+ "Breakpoint.*at.* file .*$srcfile, line.*$bp_start_test_6.*" \
+ "breakpoint at pattern 6 start"
+gdb_test "continue" "patt6.*" "continue to breakpoint at pattern 6 start"

-gdb_test "next" "longjmp caught.*" "next over patt3"
+gdb_test "next" "patt_end6.*" "next over patt6"