This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
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"