This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: Longjmp vs LD_POINTER_GUARD revisited


This is an update of Pedro's patches from this thread:

  http://sourceware.org/ml/gdb-patches/2008-05/msg00427.html

To recap, the problem is that GDB's current support for stepping over
longjmp relies on reading the jmp_buf and extracting the target PC.
There are two cases where this doesn't work:

* Where the layout of the jmp_buf changes.  It's not generally part of
the platform ABI; I know that the ARM EABI makes it explicitly opaque.
This is not a fatal flaw, since GDB is happy to grub around in
undocumented bits, but it's not great either.

* Where the jmp_buf contents are obfuscated, as done by glibc, which
"encrypts" them using a canary stored in the thread TCB.

Someone, I forget precisely who, suggested that we step through
longjmp instead of setting a breakpoint at the longjmp target.  Pedro
implemented that, but we ran into trouble with frame IDs; it's like
the bad debug info that GCC emits for epilogues, but even worse.  In
the middle of longjmp you're pretty much guaranteed to have the stack
or frame pointer clobbered in a way GDB can't recognize.

This patch updates Pedro's work to current trunk, and adds a pragmatic
hack.  If we recognize the name of the current function as definitely
related to longjmp, then we know it won't return normally, so we
should continue stepping.  For x86_64 glibc, the magic name is
"__longjmp".  Otherwise, we do a frame check as before.

This does not work without debug symbols.  If I remove the Debian
libc6-dbg package (simulated, by runtest GDBFLAGS='-ex "set
debug-file-directory /nowhere"'), the longjmp tests all stop in the
middle of __longjmp.  There's an obvious spot in this patch where
someone sufficiently motivated could add code recognition if
stop_func_name == NULL.

Any comments?  One open question with this approach is whether we
think there are platforms where this won't work (or is sufficiently
less optimal than the gdbarch_get_longjmp_target approach) that we
should preserve both methods.  Then, as Pedro suggested, we'd just
remove the Linux implementations.

I added some documentation to gdbint.texinfo about the algorithm.

If approved, there are two followup patches: remove bp_longjmp_resume
breakpoints and remove the gdbarch get_longjmp_target method.

Tested on x86_64-linux, where it fixes all of longjmp.exp with no
regressions.

-- 
Daniel Jacobowitz
CodeSourcery

2009-11-15  Pedro Alves  <pedro@codesourcery.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* infrun.c (insert_longjmp_resume_breakpoint): Delete.
	(init_thread_stepping_state): Clear stepping_through_longjmp.
	(still_in_longjmp_frame_p): New function.
	(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 frame is not needed anymore.
	Remove BPSTAT_WHAT_CLEAR_LONGJMP_RESUME case.
	(currently_stepping): Return true if stepping through longjmp.
	(currently_stepping_or_nexting_callback): Likewise.
	* gdbthread.h (struct thread_info): Add stepping_through_longjmp
	and longjmp_frame.
	* breakpoint.c (create_longjmp_master_breakpoint): Remove
	gdbarch_get_longjmp_target_p check.
	* breakpoint.h (BPSTAT_WHAT_SET_LONGJMP_RESUME): Rename to
	BPSTAT_WHAT_SKIP_LONGJMP.  All uses changed.

	* NEWS: Document improved longjmp support.

2009-11-15  Daniel Jacobowitz  <dan@codesourcery.com>

	* gdbint.texinfo (Algorithms): Update longjmp support section.

2009-11-15  Pedro Alves  <pedro@codesourcery.com>

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

Index: src/gdb/testsuite/gdb.base/longjmp.c
===================================================================
--- src.orig/gdb/testsuite/gdb.base/longjmp.c	2009-01-14 08:52:48.000000000 -0500
+++ src/gdb/testsuite/gdb.base/longjmp.c	2009-11-14 23:32:17.000000000 -0500
@@ -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	2009-01-14 08:52:48.000000000 -0500
+++ src/gdb/testsuite/gdb.base/longjmp.exp	2009-11-14 23:33:47.000000000 -0500
@@ -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.*$gdb_prompt $" {
-	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.*$gdb_prompt $" {
-	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.*$gdb_prompt $" {
-	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.*$gdb_prompt $" {
-	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"
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2009-11-13 16:59:53.000000000 -0500
+++ src/gdb/infrun.c	2009-11-15 11:25:42.000000000 -0500
@@ -2012,7 +2012,6 @@ static void insert_step_resume_breakpoin
 static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
 						  struct symtab_and_line sr_sal,
 						  struct frame_id sr_id);
-static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
 
 static void stop_stepping (struct execution_control_state *ecs);
 static void prepare_to_wait (struct execution_control_state *ecs);
@@ -2421,6 +2420,7 @@ init_thread_stepping_state (struct threa
   tss->step_after_step_resume_breakpoint = 0;
   tss->stepping_through_solib_after_catch = 0;
   tss->stepping_through_solib_catchpoints = NULL;
+  tss->stepping_through_longjmp = 0;
 }
 
 /* Return the cached copy of the last pid/waitstatus returned by
@@ -2616,6 +2616,43 @@ stepped_in_from (struct frame_info *fram
   return 0;
 }
 
+/* This function is called while we step through a call to longjmp.
+   In most cases we can detect that we are inside the call to longjmp
+   by searching for a frame ID that we saved at the start of the call;
+   if that is still on the stack, we are still stepping through the
+   call to longjmp.  However, towards the end of the call, longjmp
+   has to restore the saved stack pointer.  At this point we can
+   not trust the frame ID.  So this function must recognize the
+   affected functions by other means.  Currently we do so by
+   name; an architecture method may be needed also.
+
+   Return 1 if we are clearly inside a function which will not
+   return normally, and we should continue stepping.  Return 0
+   otherwise.  */
+
+static int
+still_in_longjmp_frame_p (const char *func)
+{
+  if (func == NULL)
+    return 0;
+
+  /* The functions we set a longjmp breakpoint on.  */
+  if (strcmp (func, "longjmp") == 0)
+    return 1;
+  if (strcmp (func, "_longjmp") == 0)
+    return 1;
+  if (strcmp (func, "siglongjmp") == 0)
+    return 1;
+  if (strcmp (func, "_longjmp") == 0)
+    return 1;
+
+  /* x86_64 glibc calls this function internally.  */
+  if (strcmp (func, "__longjmp") == 0)
+    return 1;
+
+  return 0;
+}
+
 /* Auxiliary function that handles syscall entry/return events.
    It returns 1 if the inferior should keep going (and GDB
    should ignore the event), or 0 if the event deserves to be
@@ -3608,7 +3645,8 @@ targets should add new threads to the th
 	  = !(bpstat_explains_signal (ecs->event_thread->stop_bpstat)
 	      || ecs->event_thread->trap_expected
 	      || (ecs->event_thread->step_range_end
-		  && ecs->event_thread->step_resume_breakpoint == NULL));
+		  && ecs->event_thread->step_resume_breakpoint == NULL)
+	      || ecs->event_thread->stepping_through_longjmp);
       else
 	{
 	  ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
@@ -3746,50 +3784,27 @@ 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.  */
+      case BPSTAT_WHAT_SKIP_LONGJMP:
+	/* 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");
+			      "infrun: BPSTAT_WHAT_SKIP_LONGJMP\n");
 
+	/* Step over this longjmp breakpoint.  */
 	ecs->event_thread->stepping_over_breakpoint = 1;
 
-	if (!gdbarch_get_longjmp_target_p (gdbarch)
-	    || !gdbarch_get_longjmp_target (gdbarch, 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.  */
-	delete_step_resume_breakpoint (ecs->event_thread);
-
-	/* Insert a breakpoint at resume address.  */
-	insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+	/* Store the current frame id.  This is used to check whether we
+	   are still inside the call to longjmp.  */
+	ecs->event_thread->longjmp_frame
+	  = get_stack_frame_id (get_current_frame ());
 
+	/* See you on the other side!  */
+	ecs->event_thread->stepping_through_longjmp = 1;
 	keep_going (ecs);
 	return;
 
-      case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
-        if (debug_infrun)
-	  fprintf_unfiltered (gdb_stdlog,
-			      "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
-
-	gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
-	delete_step_resume_breakpoint (ecs->event_thread);
-
-	ecs->event_thread->stop_step = 1;
-	print_stop_reason (END_STEPPING_RANGE, 0);
-	stop_stepping (ecs);
-	return;
-
       case BPSTAT_WHAT_SINGLE:
         if (debug_infrun)
 	  fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n");
@@ -4017,6 +4032,54 @@ infrun: not switching back to stepped th
       return;
     }
 
+  if (ecs->event_thread->stepping_through_longjmp)
+    {
+      struct frame_id frame_id = get_stack_frame_id (get_current_frame ());
+
+      if (still_in_longjmp_frame_p (ecs->stop_func_name)
+	  || frame_find_by_id (ecs->event_thread->longjmp_frame) != NULL)
+	{
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: stepping through longjmp\n");
+	  /* Still not there.  */
+	  keep_going (ecs);
+	  return;
+	}
+
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog, "infrun: got out of longjmp\n");
+
+      /* We made it.  */
+      ecs->event_thread->stepping_through_longjmp = 0;
+
+      /* If there's a step-resume breakpoint set, decide if we should
+	 keep stepping to the step-resume breakpoint, or if the
+	 longjmp took us outermost already, hence the step-resume
+	 breakpoint will never be hit, and we should stop now.  */
+      if (ecs->event_thread->step_resume_breakpoint)
+	{
+	  struct frame_id resume_frame_id;
+
+	  resume_frame_id = ecs->event_thread->step_resume_breakpoint->frame_id;
+	  if (! frame_id_eq (resume_frame_id, frame_id)
+	      && frame_find_by_id (resume_frame_id) != NULL)
+	    {
+	      if (debug_infrun)
+		fprintf_unfiltered (gdb_stdlog,
+				    "\
+infrun: longjmp-resume inner than step-resume\n");
+	    }
+	  else
+	    {
+	      if (debug_infrun)
+		fprintf_unfiltered (gdb_stdlog,
+				    "infrun: step-resume overran by longjmp\n");
+	      delete_step_resume_breakpoint (ecs->event_thread);
+	    }
+	}
+    }
+
   if (ecs->event_thread->step_resume_breakpoint)
     {
       if (debug_infrun)
@@ -4559,6 +4622,7 @@ currently_stepping (struct thread_info *
 {
   return ((tp->step_range_end && tp->step_resume_breakpoint == NULL)
  	  || tp->trap_expected
+	  || tp->stepping_through_longjmp
  	  || tp->stepping_through_solib_after_catch
  	  || bpstat_should_step ());
 }
@@ -4574,6 +4638,7 @@ currently_stepping_or_nexting_callback (
 
   return (tp->step_range_end
  	  || tp->trap_expected
+	  || tp->stepping_through_longjmp
  	  || tp->stepping_through_solib_after_catch);
 }
 
@@ -4777,28 +4842,6 @@ insert_step_resume_breakpoint_at_caller 
 					frame_unwind_caller_id (next_frame));
 }
 
-/* Insert a "longjmp-resume" breakpoint at PC.  This is used to set a
-   new breakpoint at the target of a jmp_buf.  The handling of
-   longjmp-resume uses the same mechanisms used for handling
-   "step-resume" breakpoints.  */
-
-static void
-insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
-{
-  /* There should never be more than one step-resume or longjmp-resume
-     breakpoint per thread, so we should never be setting a new
-     longjmp_resume_breakpoint when one is already active.  */
-  gdb_assert (inferior_thread ()->step_resume_breakpoint == NULL);
-
-  if (debug_infrun)
-    fprintf_unfiltered (gdb_stdlog,
-			"infrun: inserting longjmp-resume breakpoint at %s\n",
-			paddress (gdbarch, pc));
-
-  inferior_thread ()->step_resume_breakpoint =
-    set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
-}
-
 static void
 stop_stepping (struct execution_control_state *ecs)
 {
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-10-28 16:14:54.000000000 -0400
+++ src/gdb/gdbthread.h	2009-11-15 10:55:10.000000000 -0500
@@ -185,6 +185,13 @@ struct thread_info
   /* True if this thread has been explicitly requested to stop.  */
   int stop_requested;
 
+  /* True if a longjmp call was detected while stepping, and we're
+     single-stepping until the other end.  */
+  int stepping_through_longjmp;
+
+  /* The frame where a longjmp breakpoint was hit.  */
+  struct frame_id longjmp_frame;
+
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
 };
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2009-11-15 11:12:57.000000000 -0500
+++ src/gdb/NEWS	2009-11-15 11:17:33.000000000 -0500
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 7.0
 
+* Support for stepping and nexting over longjmp has been improved.  It now
+works independently of the architecture and supports recent versions
+of GLIBC.
+
 * New targets
 
 Xilinx MicroBlaze		microblaze-*-*
Index: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c	2009-11-15 00:19:06.000000000 -0500
+++ src/gdb/breakpoint.c	2009-11-15 11:01:01.000000000 -0500
@@ -1774,9 +1774,6 @@ create_longjmp_master_breakpoint (char *
       struct breakpoint *b;
       struct minimal_symbol *m;
 
-      if (!gdbarch_get_longjmp_target_p (get_objfile_arch (objfile)))
-	continue;
-
       set_current_program_space (pspace);
 
       m = lookup_minimal_symbol_text (func_name, objfile);
@@ -3625,7 +3622,7 @@ bpstat_what (bpstat bs)
 #define ss BPSTAT_WHAT_STOP_SILENT
 #define sn BPSTAT_WHAT_STOP_NOISY
 #define sgl BPSTAT_WHAT_SINGLE
-#define slr BPSTAT_WHAT_SET_LONGJMP_RESUME
+#define slr BPSTAT_WHAT_SKIP_LONGJMP
 #define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
 #define sr BPSTAT_WHAT_STEP_RESUME
 #define shl BPSTAT_WHAT_CHECK_SHLIBS
Index: src/gdb/breakpoint.h
===================================================================
--- src.orig/gdb/breakpoint.h	2009-11-15 11:00:06.000000000 -0500
+++ src/gdb/breakpoint.h	2009-11-15 11:01:27.000000000 -0500
@@ -561,11 +561,8 @@ enum bpstat_what_main_action
        cleanly handle BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE.  */
     BPSTAT_WHAT_SINGLE,
 
-    /* Set longjmp_resume breakpoint, remove all other breakpoints,
-       and continue.  The "remove all other breakpoints" part is required
-       if we are also stepping over another breakpoint as well as doing
-       the longjmp handling.  */
-    BPSTAT_WHAT_SET_LONGJMP_RESUME,
+    /* Step until we reach the target of a longjmp call.  */
+    BPSTAT_WHAT_SKIP_LONGJMP,
 
     /* Clear longjmp_resume breakpoint, then handle as
        BPSTAT_WHAT_KEEP_CHECKING.  */
Index: src/gdb/doc/gdbint.texinfo
===================================================================
--- src.orig/gdb/doc/gdbint.texinfo	2009-11-15 11:17:50.000000000 -0500
+++ src/gdb/doc/gdbint.texinfo	2009-11-15 11:25:35.000000000 -0500
@@ -609,15 +609,20 @@ stepping.  This is done with a few speci
 which are visible in the output of the @samp{maint info breakpoint}
 command.
 
-@findex gdbarch_get_longjmp_target
-To make this work, you need to define a function called
-@code{gdbarch_get_longjmp_target}, which will examine the
-@code{jmp_buf} structure and extract the @code{longjmp} target address.
-Since @code{jmp_buf} is target specific and typically defined in a
-target header not available to @value{GDBN}, you will need to
-determine the offset of the PC manually and return that; many targets
-define a @code{jb_pc_offset} field in the tdep structure to save the
-value once calculated.
+When @value{GDBN} detects a call to @code{longjmp}, it begins
+stepping the program.  As long as the program is still inside
+the call to @code{longjmp} (as determined by either the current
+function name or a stack frame search), @value{GDBN} continues
+stepping.  Once the program has left @code{longjmp}, @value{GDBN}
+determines whether to stop or to resume an earlier @code{next}
+opertion.
+
+In many cases you do not need any architecture-specific support
+for this feature.  You may need to augment @code{still_in_longjmp_frame_p}
+in @file{infrun.c} to recognize any functions called by @code{longjmp}
+which make unusual changes to the stack.  It can recognize
+functions by name, and could recognize additional cases
+by instruction scanning to support a stripped C library.
 
 @section Watchpoints
 @cindex watchpoints


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]