Index: breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.352 diff -u -p -r1.352 breakpoint.c --- breakpoint.c 16 Sep 2008 18:55:01 -0000 1.352 +++ breakpoint.c 1 Oct 2008 23:41:29 -0000 @@ -404,6 +404,18 @@ set_breakpoint_count (int num) value_from_longest (builtin_type_int32, (LONGEST) num)); } +/* Used in run_command to reset syscall catchpoints fields. */ + +void +clear_syscall_catchpoints_info (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_catch_syscall) + b->syscall_number = UNKNOWN_SYSCALL; +} + /* Used in run_command to zero the hit count when a new run starts. */ void @@ -810,6 +822,9 @@ insert_catchpoint (struct ui_out *uo, vo case bp_catch_exec: target_insert_exec_catchpoint (PIDGET (inferior_ptid)); break; + case bp_catch_syscall: + target_insert_syscall_catchpoint (PIDGET (inferior_ptid)); + break; default: internal_error (__FILE__, __LINE__, _("unknown breakpoint type")); break; @@ -1250,7 +1265,8 @@ Note: automatically using hardware break else if (bpt->owner->type == bp_catch_fork || bpt->owner->type == bp_catch_vfork - || bpt->owner->type == bp_catch_exec) + || bpt->owner->type == bp_catch_exec + || bpt->owner->type == bp_catch_syscall) { struct gdb_exception e = catch_exception (uiout, insert_catchpoint, bpt->owner, RETURN_MASK_ERROR); @@ -1708,6 +1724,9 @@ remove_breakpoint (struct bp_location *b case bp_catch_exec: val = target_remove_exec_catchpoint (PIDGET (inferior_ptid)); break; + case bp_catch_syscall: + val = target_remove_syscall_catchpoint (PIDGET (inferior_ptid)); + break; default: warning (_("Internal error, %s line %d."), __FILE__, __LINE__); break; @@ -1957,7 +1976,8 @@ ep_is_catchpoint (struct breakpoint *ep) || (ep->type == bp_catch_unload) || (ep->type == bp_catch_fork) || (ep->type == bp_catch_vfork) - || (ep->type == bp_catch_exec); + || (ep->type == bp_catch_exec) + || (ep->type == bp_catch_syscall); /* ??rehrauer: Add more kinds here, as are implemented... */ } @@ -2278,6 +2298,13 @@ print_it_typical (bpstat bs) struct cleanup *old_chain, *ui_out_chain; struct breakpoint *b; const struct bp_location *bl; + /* Used for "catch syscall". + + This is needed because we want to know in which state a + syscall is. It can be in the TARGET_WAITKIND_SYSCALL_ENTRY + or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we + must print "called syscall" or "returned from syscall". */ + struct thread_info *th_info = find_thread_pid (inferior_ptid); struct ui_stream *stb; int bp_temp = 0; stb = ui_out_stream_new (uiout); @@ -2329,6 +2356,14 @@ print_it_typical (bpstat bs) return PRINT_NOTHING; break; + case bp_entry_breakpoint: + /* Not sure how we will get here. + GDB should not stop for these breakpoints. */ + internal_error (__FILE__, __LINE__, + _("Entry Breakpoint: gdb should not stop!\n")); + return PRINT_NOTHING; + break; + case bp_overlay_event: /* By analogy with the thread event, GDB should not stop for these. */ printf_filtered (_("Overlay Event Breakpoint: gdb should not stop!\n")); @@ -2375,6 +2410,17 @@ print_it_typical (bpstat bs) return PRINT_SRC_AND_LOC; break; + case bp_catch_syscall: + annotate_catchpoint (b->number); + printf_filtered (_("\nCatchpoint %d (%s syscall '%s ()'), "), + b->number, + (th_info->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY) + ? "called" : "returned from", + gdbarch_syscall_name_from_number (current_gdbarch, + b->syscall_number)); + return PRINT_SRC_AND_LOC; + break; + case bp_watchpoint: case bp_hardware_watchpoint: annotate_watchpoint (b->number); @@ -2795,7 +2841,8 @@ bpstat_check_location (const struct bp_l && b->type != bp_hardware_breakpoint && b->type != bp_catch_fork && b->type != bp_catch_vfork - && b->type != bp_catch_exec) /* a non-watchpoint bp */ + && b->type != bp_catch_exec + && b->type != bp_catch_syscall) /* a non-watchpoint bp */ { if (bl->address != bp_addr) /* address doesn't match */ return 0; @@ -2869,6 +2916,25 @@ bpstat_check_location (const struct bp_l && !inferior_has_execd (inferior_ptid, &b->exec_pathname)) return 0; + /* We must check if we are catching specific syscalls in this breakpoint. + If we are, then we must guarantee that the called syscall is the same + syscall we are catching. */ + if (b->type == bp_catch_syscall) + { + int syscall_number; + if (!inferior_has_called_syscall (inferior_ptid, &syscall_number)) + return 0; + /* Now, checking if the syscall is the same. */ + if (b->syscall_to_be_caught != CATCHING_ANY_SYSCALL + && b->syscall_to_be_caught != syscall_number) + /* Not the same. */ + return 0; + + /* It's the same syscall. We can update the breakpoint struct + with the correct information. */ + b->syscall_number = syscall_number; + } + return 1; } @@ -3212,6 +3278,9 @@ bpstat_what (bpstat bs) /* We caught a shared library event. */ catch_shlib_event, + /* We are in a entry breakpoint. */ + entry_breakpoint, + /* This is just used to count how many enums there are. */ class_last }; @@ -3228,6 +3297,7 @@ bpstat_what (bpstat bs) #define sr BPSTAT_WHAT_STEP_RESUME #define shl BPSTAT_WHAT_CHECK_SHLIBS #define shlr BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK +#define entrybp BPSTAT_WHAT_ENTRY_BREAKPOINT /* "Can't happen." Might want to print an error message. abort() is not out of the question, but chances are GDB is just @@ -3272,30 +3342,33 @@ bpstat_what (bpstat bs) table[(int) class_last][(int) BPSTAT_WHAT_LAST] = { /* old action */ - /* kc ss sn sgl slr clr sr shl shlr + /* kc ss sn sgl slr clr sr shl shlr entrybp */ /*no_effect */ - {kc, ss, sn, sgl, slr, clr, sr, shl, shlr}, + {kc, ss, sn, sgl, slr, clr, sr, shl, shlr, shlr}, /*wp_silent */ - {ss, ss, sn, ss, ss, ss, sr, shl, shlr}, + {ss, ss, sn, ss, ss, ss, sr, shl, shlr, shlr}, /*wp_noisy */ - {sn, sn, sn, sn, sn, sn, sr, shl, shlr}, + {sn, sn, sn, sn, sn, sn, sr, shl, shlr, shlr}, /*bp_nostop */ - {sgl, ss, sn, sgl, slr, slr, sr, shl, shlr}, + {sgl, ss, sn, sgl, slr, slr, sr, shl, shlr, shlr}, /*bp_silent */ - {ss, ss, sn, ss, ss, ss, sr, shl, shlr}, + {ss, ss, sn, ss, ss, ss, sr, shl, shlr, shlr}, /*bp_noisy */ - {sn, sn, sn, sn, sn, sn, sr, shl, shlr}, + {sn, sn, sn, sn, sn, sn, sr, shl, shlr, shlr}, /*long_jump */ - {slr, ss, sn, slr, slr, err, sr, shl, shlr}, + {slr, ss, sn, slr, slr, err, sr, shl, shlr, shlr}, /*long_resume */ - {clr, ss, sn, err, err, err, sr, shl, shlr}, + {clr, ss, sn, err, err, err, sr, shl, shlr, shlr}, /*step_resume */ - {sr, sr, sr, sr, sr, sr, sr, sr, sr}, + {sr, sr, sr, sr, sr, sr, sr, sr, sr, sr}, /*shlib */ - {shl, shl, shl, shl, shl, shl, sr, shl, shlr}, + {shl, shl, shl, shl, shl, shl, sr, shl, shlr, shl}, /*catch_shlib */ - {shlr, shlr, shlr, shlr, shlr, shlr, sr, shlr, shlr} + {shlr, shlr, shlr, shlr, shlr, shlr, sr, shlr, shlr, shlr}, +/* entry_breakpoint */ + {entrybp, entrybp, entrybp, entrybp, entrybp, entrybp, sr, entrybp, + entrybp, entrybp} }; #undef kc @@ -3309,6 +3382,7 @@ bpstat_what (bpstat bs) #undef ts #undef shl #undef shlr +#undef entrybp enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING; struct bpstat_what retval; @@ -3396,6 +3470,7 @@ bpstat_what (bpstat bs) case bp_catch_fork: case bp_catch_vfork: case bp_catch_exec: + case bp_catch_syscall: if (bs->stop) { if (bs->print) @@ -3414,6 +3489,12 @@ bpstat_what (bpstat bs) bs_class = bp_silent; retval.call_dummy = 1; break; + case bp_entry_breakpoint: + if (bs->stop) + bs_class = entry_breakpoint; + else + bs_class = no_effect; + break; } current_action = table[(int) bs_class][(int) current_action]; } @@ -3577,7 +3658,9 @@ print_one_breakpoint_location (struct br {bp_catch_unload, "catch unload"}, {bp_catch_fork, "catch fork"}, {bp_catch_vfork, "catch vfork"}, - {bp_catch_exec, "catch exec"} + {bp_catch_exec, "catch exec"}, + {bp_catch_syscall, "catch syscall"}, + {bp_entry_breakpoint, "entry breakpoint"} }; static char bpenables[] = "nynny"; @@ -3743,6 +3826,23 @@ print_one_breakpoint_location (struct br } break; + case bp_catch_syscall: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + ui_out_text (uiout, "syscall \""); + if (b->syscall_number != UNKNOWN_SYSCALL) + ui_out_field_string (uiout, "what", + gdbarch_syscall_name_from_number (current_gdbarch, + b->syscall_number)); + else + ui_out_field_string (uiout, "what", ""); + ui_out_text (uiout, "\" "); + break; + case bp_breakpoint: case bp_hardware_breakpoint: case bp_until: @@ -3755,6 +3855,7 @@ print_one_breakpoint_location (struct br case bp_shlib_event: case bp_thread_event: case bp_overlay_event: + case bp_entry_breakpoint: if (addressprint) { annotate_field (4); @@ -3944,6 +4045,7 @@ user_settable_breakpoint (const struct b || b->type == bp_catch_fork || b->type == bp_catch_vfork || b->type == bp_catch_exec + || b->type == bp_catch_syscall || b->type == bp_hardware_breakpoint || b->type == bp_watchpoint || b->type == bp_read_watchpoint @@ -4150,6 +4252,7 @@ set_default_breakpoint (int valid, CORE_ bp_hardware_watchpoint bp_read_watchpoint bp_access_watchpoint + bp_catch_syscall bp_catch_exec bp_catch_fork bp_catch_vork */ @@ -4163,6 +4266,7 @@ breakpoint_address_is_meaningful (struct && type != bp_hardware_watchpoint && type != bp_read_watchpoint && type != bp_access_watchpoint + && type != bp_catch_syscall && type != bp_catch_exec && type != bp_catch_fork && type != bp_catch_vfork); @@ -4282,7 +4386,8 @@ adjust_breakpoint_address (CORE_ADDR bpa || bptype == bp_access_watchpoint || bptype == bp_catch_fork || bptype == bp_catch_vfork - || bptype == bp_catch_exec) + || bptype == bp_catch_exec + || bptype == bp_catch_syscall) { /* Watchpoints and the various bp_catch_* eventpoints should not have their addresses modified. */ @@ -4333,6 +4438,7 @@ allocate_bp_location (struct breakpoint case bp_watchpoint_scope: case bp_call_dummy: case bp_shlib_event: + case bp_entry_breakpoint: case bp_thread_event: case bp_overlay_event: case bp_catch_load: @@ -4351,6 +4457,7 @@ allocate_bp_location (struct breakpoint case bp_catch_fork: case bp_catch_vfork: case bp_catch_exec: + case bp_catch_syscall: loc->loc_type = bp_loc_other; break; default: @@ -4396,6 +4503,8 @@ set_raw_breakpoint_without_location (enu b->triggered_dll_pathname = NULL; b->forked_inferior_pid = null_ptid; b->exec_pathname = NULL; + b->syscall_to_be_caught = CATCHING_ANY_SYSCALL; + b->syscall_number = UNKNOWN_SYSCALL; b->ops = NULL; b->condition_not_parsed = 0; @@ -4618,6 +4727,31 @@ disable_overlay_breakpoints (void) } } +int +create_entry_breakpoint () +{ + CORE_ADDR taddr, entry_addr; + struct breakpoint *b; + + taddr = entry_point_address (); + /* Make certain that the address points at real code, and not a + function descriptor. */ + entry_addr = gdbarch_convert_from_func_ptr_addr (current_gdbarch, taddr, + ¤t_target); + + /* Setting the breakpoint */ + b = create_internal_breakpoint (entry_addr, bp_entry_breakpoint); + + b->enable_state = bp_enabled; + b->disposition = disp_del; + /* addr_string has to be used or breakpoint_re_set will delete me. */ + b->addr_string = xstrprintf ("AT_ENTRY (0x%s)", paddr (entry_addr)); + + update_global_location_list (1); + + return 1; +} + struct breakpoint * create_thread_event_breakpoint (CORE_ADDR address) { @@ -4817,6 +4951,31 @@ create_exec_event_catchpoint (int tempfl mention (b); } +static void +create_syscall_event_catchpoint (int tempflag, int syscall_number) +{ + struct symtab_and_line sal; + struct breakpoint *b; + int thread = -1; /* All threads. */ + + init_sal (&sal); + + b = set_raw_breakpoint (sal, bp_catch_syscall); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond_string = NULL; + b->thread = thread; + b->syscall_to_be_caught = syscall_number; + /* We still don't know the syscall that will be caught :-). */ + b->syscall_number = UNKNOWN_SYSCALL; + b->addr_string = NULL; + b->enable_state = bp_enabled; + b->disposition = tempflag ? disp_del : disp_donttouch; + update_global_location_list (1); + + mention (b); +} + static int hw_breakpoint_used_count (void) { @@ -5034,6 +5193,16 @@ mention (struct breakpoint *b) printf_filtered (_("Catchpoint %d (exec)"), b->number); break; + case bp_catch_syscall: + if (b->syscall_to_be_caught != CATCHING_ANY_SYSCALL) + printf_filtered (_("Catchpoint %d (syscall '%s ()')"), + b->number, + gdbarch_syscall_name_from_number (current_gdbarch, + b->syscall_to_be_caught)); + else + printf_filtered (_("Catchpoint %d (syscall)"), + b->number); + break; case bp_until: case bp_finish: @@ -5045,6 +5214,7 @@ mention (struct breakpoint *b) case bp_shlib_event: case bp_thread_event: case bp_overlay_event: + case bp_entry_breakpoint: break; } @@ -6795,6 +6965,36 @@ catch_ada_exception_command (char *arg, from_tty); } +/* Implement the "catch syscall" command. */ + +static void +catch_syscall_command_1 (char *arg, int from_tty, struct cmd_list_element *command) +{ + int tempflag; + int syscall_number = CATCHING_ANY_SYSCALL; + + tempflag = get_cmd_context (command) == CATCH_TEMPORARY; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch syscall + catch syscall + + Let's check if there's a syscall name. */ + + if (arg != NULL) + { + syscall_number = gdbarch_syscall_number_from_name (current_gdbarch, + (const char *) arg); + if (syscall_number == UNKNOWN_SYSCALL) + error (_("Invalid syscall name '%s'."), arg); + } + + /* Now let's create the catchpoint */ + create_syscall_event_catchpoint (tempflag, syscall_number); +} + /* Implement the "catch assert" command. */ static void @@ -7312,6 +7512,7 @@ delete_command (char *arg, int from_tty) b->type != bp_shlib_event && b->type != bp_thread_event && b->type != bp_overlay_event && + b->type != bp_entry_breakpoint && b->number >= 0) { breaks_to_delete = 1; @@ -7329,6 +7530,7 @@ delete_command (char *arg, int from_tty) b->type != bp_shlib_event && b->type != bp_thread_event && b->type != bp_overlay_event && + b->type != bp_entry_breakpoint && b->number >= 0) delete_breakpoint (b); } @@ -7620,6 +7822,7 @@ breakpoint_re_set_one (void *bint) case bp_catch_fork: case bp_catch_vfork: case bp_catch_exec: + case bp_catch_syscall: break; default: @@ -7639,6 +7842,9 @@ breakpoint_re_set_one (void *bint) Once it is set up, we do not want to touch it. */ case bp_thread_event: + /* Same for this one */ + case bp_entry_breakpoint: + /* Keep temporary breakpoints, which can be encountered when we step over a dlopen call and SOLIB_ADD is resetting the breakpoints. Otherwise these should have been blown away via the cleanup chain @@ -7893,6 +8099,7 @@ disable_command (char *args, int from_tt case bp_catch_fork: case bp_catch_vfork: case bp_catch_exec: + case bp_catch_syscall: case bp_hardware_breakpoint: case bp_watchpoint: case bp_hardware_watchpoint: @@ -8027,6 +8234,7 @@ enable_command (char *args, int from_tty case bp_catch_fork: case bp_catch_vfork: case bp_catch_exec: + case bp_catch_syscall: case bp_hardware_breakpoint: case bp_watchpoint: case bp_hardware_watchpoint: @@ -8209,6 +8417,45 @@ single_step_breakpoint_inserted_here_p ( return 0; } +/* Returns 0 if 'bp' is NOT a syscall catchpoint, + non-zero otherwise. */ +static int +is_syscall_catchpoint_enabled (struct breakpoint *bp) +{ + if (bp->type == bp_catch_syscall + && bp->enable_state != bp_disabled + && bp->enable_state != bp_call_disabled) + return 1; + else + return 0; +} + +int +catch_syscall_enabled (void) +{ + struct breakpoint *bp; + + ALL_BREAKPOINTS (bp) + if (is_syscall_catchpoint_enabled (bp)) + return 1; + + return 0; +} + +int +catching_syscall_number (int syscall_number) +{ + struct breakpoint *bp; + + ALL_BREAKPOINTS (bp) + if (is_syscall_catchpoint_enabled (bp)) + if (bp->syscall_to_be_caught == syscall_number + || bp->syscall_to_be_caught == CATCHING_ANY_SYSCALL) + return 1; + + return 0; +} + /* This help string is used for the break, hbreak, tbreak and thbreak commands. It is defined as a macro to prevent duplication. @@ -8549,6 +8796,12 @@ With an argument, catch only exceptions catch_exec_command_1, CATCH_PERMANENT, CATCH_TEMPORARY); + add_catch_command ("syscall", _("\ +Catch calls to syscalls.\n\ +With an argument, catch only calls of that syscall."), + catch_syscall_command_1, + CATCH_PERMANENT, + CATCH_TEMPORARY); add_catch_command ("load", _("\ Catch library loads.\n\ With an argument, catch only loads of that library."), @@ -8595,7 +8848,6 @@ an expression is either read or written. add_info ("watchpoints", breakpoints_info, _("Synonym for ``info breakpoints''.")); - /* XXX: cagney/2005-02-23: This should be a boolean, and should respond to changes - contrary to the description. */ add_setshow_zinteger_cmd ("can-use-hw-watchpoints", class_support, Index: breakpoint.h =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.h,v retrieving revision 1.79 diff -u -p -r1.79 breakpoint.h --- breakpoint.h 22 Sep 2008 15:26:53 -0000 1.79 +++ breakpoint.h 1 Oct 2008 23:41:29 -0000 @@ -32,6 +32,11 @@ struct block; arrays that should be independent of the target architecture. */ #define BREAKPOINT_MAX 16 + +/* A number to represent wether we are catching any syscalls. */ + +#define CATCHING_ANY_SYSCALL (-1) + /* Type of breakpoint. */ /* FIXME In the future, we should fold all other breakpoint-like things into @@ -127,6 +132,14 @@ enum bptype bp_catch_fork, bp_catch_vfork, bp_catch_exec, + + /* This is not really a breakpoint, but the catchpoint which implements + the "catch syscall" functionality. */ + bp_catch_syscall, + + /* This type is used to signal an internal breakpoint located at + the AT_ENTRY address. */ + bp_entry_breakpoint, }; /* States of enablement of breakpoint. */ @@ -455,6 +468,21 @@ struct breakpoint triggered. */ char *exec_pathname; + /* Syscall number used for the 'catch syscall' feature. + If no syscall has been called, its value is UNKNOWN_SYSCALL. + Otherwise, it holds the system call number in the target. + + This field is only valid immediately after this catchpoint has + triggered. */ + int syscall_number; + + /* This field is used when we are "filtering" the syscalls + (i.e., when the user types "catch syscall ". + + It stores the syscall number in case we are in the "filter mode", + or CATCHING_ANY_SYSCALL otherwise. */ + int syscall_to_be_caught; + /* Methods associated with this breakpoint. */ struct breakpoint_ops *ops; @@ -536,6 +564,10 @@ enum bpstat_what_main_action resume out of the dynamic linker's callback, stop and print. */ BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK, + /* This internal breakpoint is used syscall catchpoints only after the + shell and the dynamic linker have already ran. */ + BPSTAT_WHAT_ENTRY_BREAKPOINT, + /* This is just used to keep track of how many enums there are. */ BPSTAT_WHAT_LAST }; @@ -805,6 +837,8 @@ extern void enable_watchpoints_after_int extern enum command_control_type commands_from_control_command (char *arg, struct command_line *cmd); +extern void clear_syscall_catchpoints_info (void); + extern void clear_breakpoint_hit_counts (void); extern int get_number (char **); @@ -884,4 +918,26 @@ extern int breakpoints_always_inserted_m in our opinion won't ever trigger. */ extern void breakpoint_retire_moribund (void); +/* Checks if we are catching syscalls or not. + Returns 0 if not, greater than 0 if we are. */ +extern int catch_syscall_enabled (void); + +/* Checks if we are catching syscalls with the specific + syscall_number. Used for "filtering" the catchpoints. + Returns 0 if not, greater than 0 if we are. */ +extern int catching_syscall_number (int syscall_number); + +/* Function used to set an internal breakpoint at the AT_ENTRY + (a.k.a. the entry point of the inferior). + + This is currently needed for us to know when to start setting + up catchpoints for syscalls in the inferior. If we don't do that, + then we would set a "catch syscall" too early, which would + catch syscalls from ld.so and/or libc (and we don't want that). + + Returns zero if there was an error setting this breakpoint, + or 1 if everything went OK. */ +extern int create_entry_breakpoint (void); + + #endif /* !defined (BREAKPOINT_H) */ Index: gdbarch.c =================================================================== RCS file: /cvs/src/src/gdb/gdbarch.c,v retrieving revision 1.438 diff -u -p -r1.438 gdbarch.c --- gdbarch.c 11 Sep 2008 14:26:59 -0000 1.438 +++ gdbarch.c 1 Oct 2008 23:41:29 -0000 @@ -240,6 +240,9 @@ struct gdbarch gdbarch_target_signal_from_host_ftype *target_signal_from_host; gdbarch_target_signal_to_host_ftype *target_signal_to_host; gdbarch_record_special_symbol_ftype *record_special_symbol; + gdbarch_get_syscall_number_ftype *get_syscall_number; + gdbarch_syscall_name_from_number_ftype *syscall_name_from_number; + gdbarch_syscall_number_from_name_ftype *syscall_number_from_name; }; @@ -371,6 +374,9 @@ struct gdbarch startup_gdbarch = default_target_signal_from_host, /* target_signal_from_host */ default_target_signal_to_host, /* target_signal_to_host */ 0, /* record_special_symbol */ + 0, /* get_syscall_number */ + 0, /* syscall_name_from_number */ + 0, /* syscall_number_from_name */ /* startup_gdbarch() */ }; @@ -623,6 +629,9 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of target_signal_from_host, invalid_p == 0 */ /* Skip verify of target_signal_to_host, invalid_p == 0 */ /* Skip verify of record_special_symbol, has predicate */ + /* Skip verify of get_syscall_number, has predicate */ + /* Skip verify of syscall_name_from_number, has predicate */ + /* Skip verify of syscall_number_from_name, has predicate */ buf = ui_file_xstrdup (log, &dummy); make_cleanup (xfree, buf); if (strlen (buf) > 0) @@ -832,6 +841,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s "gdbarch_dump: get_longjmp_target = <0x%lx>\n", (long) gdbarch->get_longjmp_target); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_get_syscall_number_p() = %d\n", + gdbarch_get_syscall_number_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: get_syscall_number = <0x%lx>\n", + (long) gdbarch->get_syscall_number); + fprintf_unfiltered (file, "gdbarch_dump: have_nonsteppable_watchpoint = %s\n", plongest (gdbarch->have_nonsteppable_watchpoint)); fprintf_unfiltered (file, @@ -1051,6 +1066,18 @@ gdbarch_dump (struct gdbarch *gdbarch, s "gdbarch_dump: static_transform_name = <0x%lx>\n", (long) gdbarch->static_transform_name); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_syscall_name_from_number_p() = %d\n", + gdbarch_syscall_name_from_number_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: syscall_name_from_number = <0x%lx>\n", + (long) gdbarch->syscall_name_from_number); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_syscall_number_from_name_p() = %d\n", + gdbarch_syscall_number_from_name_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: syscall_number_from_name = <0x%lx>\n", + (long) gdbarch->syscall_number_from_name); + fprintf_unfiltered (file, "gdbarch_dump: target_desc = %s\n", plongest ((long) gdbarch->target_desc)); fprintf_unfiltered (file, @@ -3237,6 +3264,78 @@ set_gdbarch_record_special_symbol (struc gdbarch->record_special_symbol = record_special_symbol; } +int +gdbarch_get_syscall_number_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->get_syscall_number != NULL; +} + +LONGEST +gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->get_syscall_number != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_get_syscall_number called\n"); + return gdbarch->get_syscall_number (gdbarch, ptid); +} + +void +set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, + gdbarch_get_syscall_number_ftype get_syscall_number) +{ + gdbarch->get_syscall_number = get_syscall_number; +} + +int +gdbarch_syscall_name_from_number_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->syscall_name_from_number != NULL; +} + +const char * +gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, int syscall_number) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->syscall_name_from_number != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_syscall_name_from_number called\n"); + return gdbarch->syscall_name_from_number (gdbarch, syscall_number); +} + +void +set_gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, + gdbarch_syscall_name_from_number_ftype syscall_name_from_number) +{ + gdbarch->syscall_name_from_number = syscall_name_from_number; +} + +int +gdbarch_syscall_number_from_name_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->syscall_number_from_name != NULL; +} + +int +gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, const char *syscall_name) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->syscall_number_from_name != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_syscall_number_from_name called\n"); + return gdbarch->syscall_number_from_name (gdbarch, syscall_name); +} + +void +set_gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, + gdbarch_syscall_number_from_name_ftype syscall_number_from_name) +{ + gdbarch->syscall_number_from_name = syscall_number_from_name; +} + /* Keep a registry of per-architecture data-pointers required by GDB modules. */ Index: gdbarch.h =================================================================== RCS file: /cvs/src/src/gdb/gdbarch.h,v retrieving revision 1.389 diff -u -p -r1.389 gdbarch.h --- gdbarch.h 11 Sep 2008 14:26:59 -0000 1.389 +++ gdbarch.h 1 Oct 2008 23:41:29 -0000 @@ -811,6 +811,37 @@ typedef void (gdbarch_record_special_sym extern void gdbarch_record_special_symbol (struct gdbarch *gdbarch, struct objfile *objfile, asymbol *sym); extern void set_gdbarch_record_special_symbol (struct gdbarch *gdbarch, gdbarch_record_special_symbol_ftype *record_special_symbol); +/* Functions for the 'catch syscall' feature. + Get architecture-specific system calls information from registers. */ + +extern int gdbarch_get_syscall_number_p (struct gdbarch *gdbarch); + +typedef LONGEST (gdbarch_get_syscall_number_ftype) (struct gdbarch *gdbarch, ptid_t ptid); +extern LONGEST gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid); +extern void set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, gdbarch_get_syscall_number_ftype *get_syscall_number); + +/* Translate a syscall number to its corresponding name. */ + +extern int gdbarch_syscall_name_from_number_p (struct gdbarch *gdbarch); + +typedef const char * (gdbarch_syscall_name_from_number_ftype) (struct gdbarch *gdbarch, int syscall_number); +extern const char * gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, int syscall_number); +extern void set_gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, gdbarch_syscall_name_from_number_ftype *syscall_name_from_number); + +/* Translate a syscall name to its corresponding number. + + This function must return the syscall number if found, or + UNKNOWN_SYSCALL if not found. */ + +extern int gdbarch_syscall_number_from_name_p (struct gdbarch *gdbarch); + +typedef int (gdbarch_syscall_number_from_name_ftype) (struct gdbarch *gdbarch, const char *syscall_name); +extern int gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, const char *syscall_name); +extern void set_gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, gdbarch_syscall_number_from_name_ftype *syscall_number_from_name); + +/* Definition for an unknown syscall, used basically in error-cases. */ +#define UNKNOWN_SYSCALL (-1) + extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch); Index: gdbarch.sh =================================================================== RCS file: /cvs/src/src/gdb/gdbarch.sh,v retrieving revision 1.479 diff -u -p -r1.479 gdbarch.sh --- gdbarch.sh 11 Sep 2008 14:27:00 -0000 1.479 +++ gdbarch.sh 1 Oct 2008 23:41:29 -0000 @@ -707,6 +707,20 @@ m:int:target_signal_to_host:enum target_ # Record architecture-specific information from the symbol table. M:void:record_special_symbol:struct objfile *objfile, asymbol *sym:objfile, sym + +# Functions for the 'catch syscall' feature. + +# Get architecture-specific system calls information from registers. +M:LONGEST:get_syscall_number:ptid_t ptid:ptid + +# Translate a syscall number to its corresponding name. +M:const char *:syscall_name_from_number:int syscall_number:syscall_number + +# Translate a syscall name to its corresponding number. +# +# This function must return the syscall number if found, or +# UNKNOWN_SYSCALL if not found. +M:int:syscall_number_from_name:const char *syscall_name:syscall_name EOF } @@ -888,6 +902,9 @@ done # close it off cat <to_follow_fork = inf_child_follow_fork; t->to_insert_exec_catchpoint = inf_child_insert_exec_catchpoint; t->to_remove_exec_catchpoint = inf_child_remove_exec_catchpoint; + t->to_insert_syscall_catchpoint = inf_child_insert_syscall_catchpoint; + t->to_remove_syscall_catchpoint = inf_child_remove_syscall_catchpoint; t->to_can_run = inf_child_can_run; t->to_pid_to_exec_file = inf_child_pid_to_exec_file; t->to_stratum = process_stratum; Index: inf-ptrace.c =================================================================== RCS file: /cvs/src/src/gdb/inf-ptrace.c,v retrieving revision 1.49 diff -u -p -r1.49 inf-ptrace.c --- inf-ptrace.c 22 Sep 2008 15:21:30 -0000 1.49 +++ inf-ptrace.c 1 Oct 2008 23:41:29 -0000 @@ -355,13 +355,19 @@ static void inf_ptrace_resume (ptid_t ptid, int step, enum target_signal signal) { pid_t pid = ptid_get_pid (ptid); - int request = PT_CONTINUE; + int request; if (pid == -1) /* Resume all threads. Traditionally ptrace() only supports single-threaded processes, so simply resume the inferior. */ pid = ptid_get_pid (inferior_ptid); + if (target_passed_by_entrypoint () > 0 + && catch_syscall_enabled () > 0) + request = PT_SYSCALL; + else + request = PT_CONTINUE; + if (step) { /* If this system does not support PT_STEP, a higher level Index: infcmd.c =================================================================== RCS file: /cvs/src/src/gdb/infcmd.c,v retrieving revision 1.212 diff -u -p -r1.212 infcmd.c --- infcmd.c 22 Sep 2008 15:20:08 -0000 1.212 +++ infcmd.c 1 Oct 2008 23:41:30 -0000 @@ -450,6 +450,11 @@ run_command_1 (char *args, int from_tty, init_wait_for_inferior (); clear_breakpoint_hit_counts (); + /* If we already caught a syscall catchpoint, then reset its + syscall_number information because we are starting all over + again. */ + clear_syscall_catchpoints_info (); + /* Clean up any leftovers from other runs. Some other things from this function should probably be moved into target_pre_inferior. */ target_pre_inferior (from_tty); Index: infrun.c =================================================================== RCS file: /cvs/src/src/gdb/infrun.c,v retrieving revision 1.322 diff -u -p -r1.322 infrun.c --- infrun.c 22 Sep 2008 15:26:53 -0000 1.322 +++ infrun.c 1 Oct 2008 23:41:30 -0000 @@ -964,7 +964,7 @@ a command like `return' or `jump' to con } } - /* If there were any forks/vforks/execs that were caught and are + /* If there were any forks/vforks/execs/syscalls that were caught and are now to be followed, then do so. */ switch (pending_follow.kind) { @@ -980,6 +980,11 @@ a command like `return' or `jump' to con pending_follow.kind = TARGET_WAITKIND_SPURIOUS; break; + case TARGET_WAITKIND_SYSCALL_ENTRY: + case TARGET_WAITKIND_SYSCALL_RETURN: + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + break; + default: break; } @@ -1386,8 +1391,8 @@ init_wait_for_inferior (void) breakpoint_init_inferior (inf_starting); - /* The first resume is not following a fork/vfork/exec. */ - pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */ + /* The first resume is not following a fork/vfork/exec/syscall. */ + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */ clear_proceed_status (); @@ -1831,6 +1836,50 @@ ensure_not_running (void) error_is_running (); } +/* 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 + processed. */ +static int +deal_with_syscall_event (struct execution_control_state *ecs) +{ + int syscall_number = gdbarch_get_syscall_number (current_gdbarch, + ecs->ptid); + if (catch_syscall_enabled () > 0 + && catching_syscall_number (syscall_number) > 0) + { + ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP; + pending_follow.kind = ecs->ws.kind; + + if (!ptid_equal (ecs->ptid, inferior_ptid)) + { + context_switch (ecs->ptid); + reinit_frame_cache (); + } + + stop_pc = read_pc (); + + ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid); + + ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat); + + /* If no catchpoint triggered for this, then keep going. */ + if (ecs->random_signal) + { + ecs->event_thread->stop_signal = TARGET_SIGNAL_0; + keep_going (ecs); + return 1; + } + return 0; + } + else + { + resume (0, TARGET_SIGNAL_0); + prepare_to_wait (ecs); + return 1; + } +} + /* Given an execution control state that has been freshly filled in by an event from the inferior, figure out what it means and take appropriate action. */ @@ -2119,9 +2168,11 @@ handle_inferior_event (struct execution_ case TARGET_WAITKIND_SYSCALL_ENTRY: if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n"); - resume (0, TARGET_SIGNAL_0); - prepare_to_wait (ecs); - return; + /* Getting the current syscall number */ + if (deal_with_syscall_event (ecs) != 0) + return; + goto process_event_stop_test; + break; /* Before examining the threads further, step this thread to get it entirely out of the syscall. (We get notice of the @@ -2131,9 +2182,10 @@ handle_inferior_event (struct execution_ case TARGET_WAITKIND_SYSCALL_RETURN: if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_RETURN\n"); - target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); - prepare_to_wait (ecs); - return; + if (deal_with_syscall_event (ecs) != 0) + return; + goto process_event_stop_test; + break; case TARGET_WAITKIND_STOPPED: if (debug_infrun) @@ -2951,6 +3003,16 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( } break; + case BPSTAT_WHAT_ENTRY_BREAKPOINT: + /* We hit the AT_ENTRY breakpoint, and now we have to enable + the PTRACE_O_TRACESYSGOOD option in the inferior *if* we + are catching syscalls. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_ENTRY_BREAKPOINT\n"); + target_enable_tracesysgood (ecs->ptid); + ecs->event_thread->stepping_over_breakpoint = 1; + break; + case BPSTAT_WHAT_LAST: /* Not a real code, but listed here to shut up gcc -Wall. */ @@ -4563,6 +4625,25 @@ inferior_has_execd (ptid_t pid, char **e return 1; } +int +inferior_has_called_syscall (ptid_t pid, int *syscall_number) +{ + struct target_waitstatus last; + ptid_t last_ptid; + + get_last_target_status (&last_ptid, &last); + + if (last.kind != TARGET_WAITKIND_SYSCALL_ENTRY && + last.kind != TARGET_WAITKIND_SYSCALL_RETURN) + return 0; + + if (!ptid_equal (last_ptid, pid)) + return 0; + + *syscall_number = last.value.syscall_number; + return 1; +} + /* Oft used ptids */ ptid_t null_ptid; ptid_t minus_one_ptid; Index: target.c =================================================================== RCS file: /cvs/src/src/gdb/target.c,v retrieving revision 1.184 diff -u -p -r1.184 target.c --- target.c 22 Sep 2008 15:21:30 -0000 1.184 +++ target.c 1 Oct 2008 23:41:30 -0000 @@ -426,6 +426,10 @@ update_current_target (void) /* Do not inherit to_follow_fork. */ INHERIT (to_insert_exec_catchpoint, t); INHERIT (to_remove_exec_catchpoint, t); + INHERIT (to_passed_by_entrypoint, t); + INHERIT (to_insert_syscall_catchpoint, t); + INHERIT (to_remove_syscall_catchpoint, t); + INHERIT (to_enable_tracesysgood, t); INHERIT (to_has_exited, t); INHERIT (to_mourn_inferior, t); INHERIT (to_can_run, t); @@ -581,9 +585,21 @@ update_current_target (void) de_fault (to_insert_exec_catchpoint, (void (*) (int)) tcomplain); + de_fault (to_passed_by_entrypoint, + (int (*) (void)) + tcomplain); de_fault (to_remove_exec_catchpoint, (int (*) (int)) tcomplain); + de_fault (to_insert_syscall_catchpoint, + (void (*) (int)) + tcomplain); + de_fault (to_remove_syscall_catchpoint, + (int (*) (int)) + tcomplain); + de_fault (to_enable_tracesysgood, + (void (*) (ptid_t)) + tcomplain); de_fault (to_has_exited, (int (*) (int, int, int *)) return_zero); @@ -2532,6 +2548,12 @@ debug_to_wait (ptid_t ptid, struct targe case TARGET_WAITKIND_EXECD: fprintf_unfiltered (gdb_stdlog, "execd\n"); break; + case TARGET_WAITKIND_SYSCALL_ENTRY: + fprintf_unfiltered (gdb_stdlog, "entered syscall\n"); + break; + case TARGET_WAITKIND_SYSCALL_RETURN: + fprintf_unfiltered (gdb_stdlog, "exited syscall\n"); + break; case TARGET_WAITKIND_SPURIOUS: fprintf_unfiltered (gdb_stdlog, "spurious\n"); break; Index: target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.130 diff -u -p -r1.130 target.h --- target.h 19 Aug 2008 13:22:14 -0000 1.130 +++ target.h 1 Oct 2008 23:41:30 -0000 @@ -135,14 +135,15 @@ struct target_waitstatus { enum target_waitkind kind; - /* Forked child pid, execd pathname, exit status or signal number. */ + /* Forked child pid, execd pathname, exit status, signal number or + syscall name. */ union { int integer; enum target_signal sig; ptid_t related_pid; char *execd_pathname; - int syscall_id; + int syscall_number; } value; }; @@ -393,6 +394,10 @@ struct target_ops int (*to_follow_fork) (struct target_ops *, int); void (*to_insert_exec_catchpoint) (int); int (*to_remove_exec_catchpoint) (int); + int (*to_passed_by_entrypoint) (void); + void (*to_insert_syscall_catchpoint) (int); + int (*to_remove_syscall_catchpoint) (int); + void (*to_enable_tracesysgood) (ptid_t); int (*to_has_exited) (int, int, int *); void (*to_mourn_inferior) (void); int (*to_can_run) (void); @@ -708,6 +713,8 @@ extern int inferior_has_vforked (ptid_t extern int inferior_has_execd (ptid_t pid, char **execd_pathname); +extern int inferior_has_called_syscall (ptid_t pid, int *syscall_number); + /* From exec.c */ extern void print_section_info (struct target_ops *, bfd *); @@ -867,6 +874,24 @@ int target_follow_fork (int follow_child #define target_remove_exec_catchpoint(pid) \ (*current_target.to_remove_exec_catchpoint) (pid) +/* Has the inferior already passed through its entrypoint? */ +#define target_passed_by_entrypoint() \ + (*current_target.to_passed_by_entrypoint) () + +/* Syscall catch functions */ + +#define target_insert_syscall_catchpoint(pid) \ + (*current_target.to_insert_syscall_catchpoint) (pid) + +#define target_remove_syscall_catchpoint(pid) \ + (*current_target.to_remove_syscall_catchpoint) (pid) + +/* Enable PTRACE_O_TRACESYSGOOD in the inferior. + This is mainly used for the "catch syscall" feature. */ + +#define target_enable_tracesysgood(ptid) \ + (*current_target.to_enable_tracesysgood) (ptid) + /* Returns TRUE if PID has exited. And, also sets EXIT_STATUS to the exit code of PID, if any. */