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]

Re: RFC: Longjmp vs LD_POINTER_GUARD revisited


On Monday 16 November 2009 14:53:50, Pedro Alves wrote:
> ?- I've tried a similar hack as yours and found that I had
> ? ?to add several more "functions still within longjmp" special
> ? ?cases. ?I had tried it on netbsd and Windows too. ?NetBSD
> ? ?was also bad at needing special casing. ?I'll post the relevant
> ? ?bits of the patch in a bit, when I find it :-)
> 

Here they are...

-- 
Pedro Alves

Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2009-07-25 16:47:07.000000000 +0100
+++ src/gdb/infrun.c	2009-07-25 18:28:50.000000000 +0100
@@ -1330,6 +1330,9 @@ clear_proceed_status_thread (struct thre
 
   tp->proceed_to_finish = 0;
 
+  tp->stepping_through_longjmp = 0;
+  tp->longjmp_frame = null_frame_id;
+
   /* Discard any remaining commands or status from previous stop.  */
   bpstat_clear (&tp->stop_bpstat);
 }
@@ -3248,7 +3251,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);
@@ -3374,7 +3378,6 @@ process_event_stop_test:
 
   /* Handle cases caused by hitting a breakpoint.  */
   {
-    CORE_ADDR jmp_buf_pc;
     struct bpstat_what what;
 
     what = bpstat_what (ecs->event_thread->stop_bpstat);
@@ -3387,33 +3390,19 @@ 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->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 longjmp's frame id.  We'll single-step until an outer
+	   frame becomes current.  */
+ 	ecs->event_thread->longjmp_frame = get_frame_id (get_current_frame ());
+ 	ecs->event_thread->stepping_through_longjmp = 1;
 	keep_going (ecs);
 	return;
 
@@ -3641,6 +3630,135 @@ infrun: not switching back to stepped th
       return;
     }
 
+   if (ecs->event_thread->stepping_through_longjmp)
+     {
+       struct thread_info *tp = ecs->event_thread;
+       struct frame_info *frame = get_current_frame ();
+       struct gdbarch *gdbarch = get_frame_arch (frame);
+       struct minimal_symbol *msymbol;
+
+       /* While we're still inside longjmp, in most implementations,
+	  matching by frame id won't work, unfortunately, either
+	  because unwind info is missing the correct annotations, or
+	  because the frame parsers have a hard time with the PC, SP,
+	  and FP juggling going on inside longjmp.  Libc
+	  implementations that apply some form of mangling to
+	  jmp_buf's are the worse offenders.  There's another caveat
+	  here: if we find ourselves in code that has no symbol info
+	  whatsoever, we don't really know if we're still inside
+	  longjmp, or, if we've reached a setjmp landing site that had
+	  no debug info.  We're assuming the former.  */
+
+       if (in_solib_dynsym_resolve_code (get_frame_pc (frame)))
+	 {
+	   CORE_ADDR pc_after_resolver =
+	     gdbarch_skip_solib_resolver (get_frame_arch (frame), get_frame_pc (frame));
+
+	   if (debug_infrun)
+	     fprintf_unfiltered (gdb_stdlog, "infrun: stepped into dynsym resolve code\n");
+
+	   if (pc_after_resolver)
+	     {
+	       /* Set up a step-resume breakpoint at the address
+		  indicated by SKIP_SOLIB_RESOLVER.  */
+	       struct symtab_and_line sr_sal;
+	       init_sal (&sr_sal);
+	       sr_sal.pc = pc_after_resolver;
+
+	       insert_step_resume_breakpoint_at_sal (gdbarch,
+						     sr_sal, null_frame_id);
+	     }
+
+	   keep_going (ecs);
+	   return;
+	 }
+
+       msymbol = lookup_minimal_symbol_by_pc (get_frame_pc (frame));
+       if (msymbol == NULL)
+	 {
+	   /* We're still supposedly in longjmp, keep going.  Should
+	      perhaps check if we're still in the same module (that
+	      is, catch going out of libc.so?)  */
+	   if (debug_infrun)
+	     fprintf_unfiltered (gdb_stdlog,
+				 "infrun: stepping through longjmp (in ??\?)\n");
+	   keep_going (ecs);
+	   return;
+	 }
+       else if (strcmp (SYMBOL_LINKAGE_NAME (msymbol), "longjmp") == 0
+		|| strcmp (SYMBOL_LINKAGE_NAME (msymbol), "_longjmp") == 0
+		|| strcmp (SYMBOL_LINKAGE_NAME (msymbol), "siglongjmp") == 0
+		|| strcmp (SYMBOL_LINKAGE_NAME (msymbol), "_siglongjmp") == 0)
+	 {
+	   /* Update the recorded frame id, as it is likely to not be
+	      stable (in a perfect world, it would be, though).  */
+	   tp->longjmp_frame = get_frame_id (frame);
+
+	   /* We're still in longjmp, keep going.  */
+	   if (debug_infrun)
+	     fprintf_unfiltered (gdb_stdlog,
+				 "infrun: stepping through longjmp (in longjmp)\n");
+	   keep_going (ecs);
+	   return;
+	 }
+       /* Make this an uglier than ugly architecture specific callback
+	  "un-unwindable functions that are called by longjmp, and
+	  can't be longjmp landing sites" list?  Sigh...  */
+       else if (strcmp (SYMBOL_LINKAGE_NAME (msymbol),
+			"__pthread_cleanup_upto") == 0 /* linux */
+		/* openbsd */
+		|| strcmp (SYMBOL_LINKAGE_NAME (msymbol), "sigsetmask") == 0)
+	 {
+	   /* We're still waiting for the longjmp, keep going.  */
+	   if (debug_infrun)
+	     fprintf_unfiltered (gdb_stdlog,
+				 "infrun: stepping through longjmp (in callee)\n");
+	   keep_going (ecs);
+	   return;
+	 }
+       else if (frame_find_by_id (tp->longjmp_frame) != NULL)
+	 {
+	   /* We still have the longjmp frame in the frame chain, keep
+	      going.  In theory, we would only need this check.  */
+	   if (debug_infrun)
+	     fprintf_unfiltered (gdb_stdlog,
+				 "infrun: stepping through longjmp (in frame chain)\n");
+	   keep_going (ecs);
+	   return;
+	 }
+
+       if (debug_infrun)
+	 fprintf_unfiltered (gdb_stdlog, "infrun: got out of longjmp\n");
+
+       /* We made it.  */
+       tp->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)
+	 {
+	   if (!frame_id_eq (get_frame_id (frame),
+			     tp->step_resume_breakpoint->frame_id)
+	       && frame_find_by_id (tp->step_resume_breakpoint->frame_id))
+	     {
+	       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);
+	       stop_stepping (ecs);
+	       return;
+	     }
+	 }
+     }
+
   if (ecs->event_thread->step_resume_breakpoint)
     {
       if (debug_infrun)
@@ -4165,7 +4283,8 @@ currently_stepping (struct thread_info *
   return ((tp->step_range_end && tp->step_resume_breakpoint == NULL)
  	  || tp->trap_expected
  	  || tp->stepping_through_solib_after_catch
- 	  || bpstat_should_step ());
+ 	  || bpstat_should_step ()
+	  || tp->stepping_through_longjmp);
 }
 
 /* Returns true if any thread *but* the one passed in "data" is in the
@@ -4179,7 +4298,8 @@ currently_stepping_or_nexting_callback (
 
   return (tp->step_range_end
  	  || tp->trap_expected
- 	  || tp->stepping_through_solib_after_catch);
+ 	  || tp->stepping_through_solib_after_catch
+	  || tp->stepping_through_longjmp);
 }
 
 /* Inferior has stepped into a subroutine call with source code that
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-07-25 16:47:07.000000000 +0100
+++ src/gdb/gdbthread.h	2009-07-25 17:06:40.000000000 +0100
@@ -185,6 +185,12 @@ 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 we reach the other end.  */
+  int stepping_through_longjmp;
+  /* The frame of the longjmp we're currently handling.  */
+  struct frame_id longjmp_frame;
+
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
 };
Index: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c	2009-07-25 16:47:07.000000000 +0100
+++ src/gdb/breakpoint.c	2009-07-25 17:06:40.000000000 +0100
@@ -1532,9 +1532,6 @@ create_longjmp_master_breakpoint (char *
       struct breakpoint *b;
       struct minimal_symbol *m;
 
-      if (!gdbarch_get_longjmp_target_p (get_objfile_arch (objfile)))
-	continue;
-
       m = lookup_minimal_symbol_text (func_name, objfile);
       if (m == NULL)
         continue;
Index: src/gdb/testsuite/gdb.base/longjmp.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/longjmp.exp	2009-07-25 16:47:07.000000000 +0100
+++ src/gdb/testsuite/gdb.base/longjmp.exp	2009-07-25 17:06:40.000000000 +0100
@@ -127,4 +127,4 @@ gdb_test "break $bp_start_test_3" \
     "breakpoint at pattern 3 start"
 gdb_test "continue" "patt3.*" "continue to breakpoint at pattern 3 start"
 
-gdb_test "next" "longjmp caught.*" "next over patt3"
+gdb_test "next" "patt_end3.*" "next over patt3"


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