Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Pedro Alves <pedro@codesourcery.com>
To: gdb-patches@sourceware.org
Cc: Daniel Jacobowitz <drow@false.org>,
	 Paul Pluzhnikov <ppluzhnikov@google.com>,
	 Ulrich Weigand <uweigand@de.ibm.com>
Subject: Re: RFC: Longjmp vs LD_POINTER_GUARD revisited
Date: Mon, 16 Nov 2009 15:05:00 -0000	[thread overview]
Message-ID: <200911161504.50279.pedro@codesourcery.com> (raw)
In-Reply-To: <200911161453.50890.pedro@codesourcery.com>

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"


  parent reply	other threads:[~2009-11-16 15:05 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-11-15 17:35 Daniel Jacobowitz
2009-11-15 18:06 ` Eli Zaretskii
2009-11-15 18:30 ` Paul Pluzhnikov
2009-11-15 22:36   ` Daniel Jacobowitz
2009-11-15 23:06     ` Paul Pluzhnikov
2009-11-16 14:37       ` Daniel Jacobowitz
2009-11-16 14:55         ` Pedro Alves
2009-11-16 14:56           ` Pedro Alves
2009-11-16 15:05           ` Pedro Alves [this message]
2009-11-16 17:50           ` Daniel Jacobowitz
2009-11-15 18:39 ` Joseph S. Myers
2009-11-15 21:52   ` Mark Kettenis
2009-11-15 22:37     ` Daniel Jacobowitz
2009-11-16 15:15     ` Frank Ch. Eigler
2009-11-16 15:40       ` Paul Pluzhnikov
2009-11-16 15:43         ` Paul Pluzhnikov
2009-11-16 16:19         ` Mark Kettenis
2009-11-16 15:59       ` Mark Kettenis

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200911161504.50279.pedro@codesourcery.com \
    --to=pedro@codesourcery.com \
    --cc=drow@false.org \
    --cc=gdb-patches@sourceware.org \
    --cc=ppluzhnikov@google.com \
    --cc=uweigand@de.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox