From: Sergio Durigan Junior Date: Tue, 15 Sep 2009 03:30:08 +0000 (+0000) Subject: Implementing catch syscall. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a96d9b2e9a79e6cc7a9da9b4e5bab6fcc35f1eb4;p=binutils-gdb.git Implementing catch syscall. * amd64-linux-tdep.c: Include xml-syscall.h header, define the XML syscall name for the architecture. (amd64_linux_get_syscall_number): New function. (amd64_linux_init_abi): Register the correct functions for syscall catchpoint; set the correct syscall file name. * breakpoint.c: New include: xml-syscall.h. (set_raw_breakpoint_without_location): Setting the parameters for the catch syscall feature. (insert_catch_syscall): New. (remove_catch_syscall): New. (breakpoint_hit_catch_syscall): New. (print_it_catch_syscall): New. (print_one_catch_syscall): New. (print_mention_catch_syscall): New. (catch_syscall_breakpoint_ops): New. (syscall_catchpoint_p): New. (create_catchpoint_without_mention): New. (create_catchpoint): Modified in order to use create_catchpoint_without_mention. (create_syscall_event_catchpoint): New. (clean_up_filters): New. (catch_syscall_split_args): New. (catch_syscall_command_1): New. (delete_breakpoint): Add cleanup for catch syscall. (is_syscall_catchpoint_enabled): New. (catch_syscall_enabled): New. (catching_syscall_number): New. (catch_syscall_completer): New completer function. (add_catch_command): Add the completer function for catchpoints. * breakpoint.h (syscalls_to_be_caught): New vector. (catch_syscall_enabled): New. (catching_syscall_number): New. * gdbarch.c: Regenerated. * gdbarch.h: Regenerated. * gdbarch.sh: Add syscall catchpoint functions and structures. (get_syscall_number): New. (UNKNOWN_SYSCALL): New definition. * i386-linux-nat.c (i386_linux_resume): Select the proper request to be made for ptrace() considering if we are catching syscalls or not. * i386-linux-tdep.c: Include xml-syscall.h header, define the XML syscall name for the architecture. (i386_linux_get_syscall_number): New. (i386_linux_init_abi): Register the correct functions for syscall catchpoint; set the correct syscall file name. * inf-child.c (inf_child_set_syscall_catchpoint): New. (inf_child_target): Assign default values to target_ops. * inf-ptrace.c (inf_ptrace_resume): Select the proper request to be made for ptrace() considering if we are catching syscalls or not. * inferior.h (struct inferior): Included new variables any_syscall_count, syscalls_counts and total_syscalls_count, used to keep track of requested syscall catchpoints. * infrun.c (resume): Add syscall catchpoint. (deal_with_syscall_event): New. (handle_inferior_event): Add syscall entry/return events. (inferior_has_called_syscall): New. * linux-nat.c: Define some helpful variables to track wether we have support for the needed ptrace option. (linux_test_for_tracesysgood): New. (linux_supports_tracesysgood): New. (linux_enable_tracesysgood): New. (linux_enable_event_reporting): Save the current used ptrace options. (linux_child_post_attach): Calling linux_enable_tracesysgood. (linux_child_post_startup_inferior): Likewise. (linux_child_set_syscall_catchpoint): New function. (linux_handle_extended_wait): Handle the case which the inferior stops because it has called or returned from a syscall. (linux_target_install_ops): Install the necessary functions to handle syscall catchpoints. * linux-nat.h (struct lwp_info): Include syscall_state into the structure, which indicates if we are in a syscall entry or return. * ppc-linux-tdep.c: Include xml-syscall.h header, define the XML syscall filename for the arch. (ppc_linux_get_syscall_number): New. (ppc_linux_init_abi): Register the correct functions for syscall catchpoint; setting the correct name for the XML syscall file. * target.c (update_current_target): Update/copy functions related to syscall catchpoint. (target_waitstatus_to_string): Add syscall catchpoint entry/return events. * target.h (struct target_waitstatus): Add syscall number. (struct syscall): New struct to hold information about syscalls in the system. (struct target_ops): Add ops for syscall catchpoint. (inferior_has_called_syscall): New. (target_set_syscall_catchpoint): New. * xml-support.c (xml_fetch_content_from_file): New function, transferred from xml-tdesc.c. * xml-support.h (xml_fetch_content_from_file): New. * xml-tdesc.c (fetch_xml_from_file): Function removed; transferred to xml-support.c. (file_read_description_xml): Updated to use the new xml_fetch_content_from_file function. * syscalls/gdb-syscalls.dtd: New definition file for syscall's XML support. * syscalls/amd64-linux.xml: New file containing information about syscalls for GNU/Linux systems that use amd64 architecture. * syscalls/i386-linux.xml: New file containing information about syscalls for GNU/Linux systems that use i386 architecture. * syscalls/ppc-linux.xml: New file containing information about syscalls for GNU/Linux systems that use PPC architecture. * syscalls/ppc64-linux.xml: New file containing information about syscalls for GNU/Linux systems that use PPC64 architecture. * xml-syscall.c: New file containing functions for manipulating syscall's XML files. * xml-syscall.h: New file, exporting the functions above mentioned. * Makefile.in: Support for relocatable GDB datadir and XML syscall. * NEWS: Added information about the catch syscall feature. * doc/gdb.texinfo (Set Catchpoints): Documentation about the new feature. * testsuite/Makefile.in: Inclusion of catch-syscall object. * testsuite/gdb.base/catch-syscall.c: New file. * testsuite/gdb.base/catch-syscall.exp: New file. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ce226cc3dda..c1018455bb2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,117 @@ +2009-09-14 Sergio Durigan Junior + + * amd64-linux-tdep.c: Include xml-syscall.h header, define the XML + syscall name for the architecture. + (amd64_linux_get_syscall_number): New function. + (amd64_linux_init_abi): Register the correct functions for syscall + catchpoint; set the correct syscall file name. + * breakpoint.c: New include: xml-syscall.h. + (set_raw_breakpoint_without_location): Setting the parameters + for the catch syscall feature. + (insert_catch_syscall): New. + (remove_catch_syscall): New. + (breakpoint_hit_catch_syscall): New. + (print_it_catch_syscall): New. + (print_one_catch_syscall): New. + (print_mention_catch_syscall): New. + (catch_syscall_breakpoint_ops): New. + (syscall_catchpoint_p): New. + (create_catchpoint_without_mention): New. + (create_catchpoint): Modified in order to use + create_catchpoint_without_mention. + (create_syscall_event_catchpoint): New. + (clean_up_filters): New. + (catch_syscall_split_args): New. + (catch_syscall_command_1): New. + (delete_breakpoint): Add cleanup for catch syscall. + (is_syscall_catchpoint_enabled): New. + (catch_syscall_enabled): New. + (catching_syscall_number): New. + (catch_syscall_completer): New completer function. + (add_catch_command): Add the completer function for catchpoints. + * breakpoint.h (syscalls_to_be_caught): New vector. + (catch_syscall_enabled): New. + (catching_syscall_number): New. + * gdbarch.c: Regenerated. + * gdbarch.h: Regenerated. + * gdbarch.sh: Add syscall catchpoint functions and structures. + (get_syscall_number): New. + (UNKNOWN_SYSCALL): New definition. + * i386-linux-nat.c (i386_linux_resume): Select the proper request + to be made for ptrace() considering if we are catching syscalls + or not. + * i386-linux-tdep.c: Include xml-syscall.h header, define the XML + syscall name for the architecture. + (i386_linux_get_syscall_number): New. + (i386_linux_init_abi): Register the correct functions for syscall + catchpoint; set the correct syscall file name. + * inf-child.c (inf_child_set_syscall_catchpoint): New. + (inf_child_target): Assign default values to target_ops. + * inf-ptrace.c (inf_ptrace_resume): Select the proper request + to be made for ptrace() considering if we are catching syscalls + or not. + * inferior.h (struct inferior): Included new variables + any_syscall_count, syscalls_counts and total_syscalls_count, + used to keep track of requested syscall catchpoints. + * infrun.c (resume): Add syscall catchpoint. + (deal_with_syscall_event): New. + (handle_inferior_event): Add syscall entry/return events. + (inferior_has_called_syscall): New. + * linux-nat.c: Define some helpful variables to track wether we have + support for the needed ptrace option. + (linux_test_for_tracesysgood): New. + (linux_supports_tracesysgood): New. + (linux_enable_tracesysgood): New. + (linux_enable_event_reporting): Save the current used ptrace + options. + (linux_child_post_attach): Calling linux_enable_tracesysgood. + (linux_child_post_startup_inferior): Likewise. + (linux_child_set_syscall_catchpoint): New function. + (linux_handle_extended_wait): Handle the case which the inferior stops + because it has called or returned from a syscall. + (linux_target_install_ops): Install the necessary functions to handle + syscall catchpoints. + * linux-nat.h (struct lwp_info): Include syscall_state into the + structure, which indicates if we are in a syscall entry or return. + * ppc-linux-tdep.c: Include xml-syscall.h header, define the XML + syscall filename for the arch. + (ppc_linux_get_syscall_number): New. + (ppc_linux_init_abi): Register the correct functions for syscall + catchpoint; setting the correct name for the XML syscall file. + * target.c (update_current_target): Update/copy functions related to + syscall catchpoint. + (target_waitstatus_to_string): Add syscall catchpoint entry/return + events. + * target.h (struct target_waitstatus): Add syscall number. + (struct syscall): New struct to hold information about syscalls + in the system. + (struct target_ops): Add ops for syscall catchpoint. + (inferior_has_called_syscall): New. + (target_set_syscall_catchpoint): New. + * xml-support.c (xml_fetch_content_from_file): New function, + transferred from xml-tdesc.c. + * xml-support.h (xml_fetch_content_from_file): New. + * xml-tdesc.c (fetch_xml_from_file): Function removed; + transferred to xml-support.c. + (file_read_description_xml): Updated to use the new + xml_fetch_content_from_file function. + * syscalls/gdb-syscalls.dtd: New definition file for syscall's XML + support. + * syscalls/amd64-linux.xml: New file containing information about + syscalls for GNU/Linux systems that use amd64 architecture. + * syscalls/i386-linux.xml: New file containing information about + syscalls for GNU/Linux systems that use i386 architecture. + * syscalls/ppc-linux.xml: New file containing information about + syscalls for GNU/Linux systems that use PPC architecture. + * syscalls/ppc64-linux.xml: New file containing information about + syscalls for GNU/Linux systems that use PPC64 architecture. + * xml-syscall.c: New file containing functions for manipulating + syscall's XML files. + * xml-syscall.h: New file, exporting the functions above mentioned. + * Makefile.in: Support for relocatable GDB datadir and XML + syscall. + * NEWS: Added information about the catch syscall feature. + 2009-09-14 Doug Evans * target.c (memory_xfer_partial): Only update dcache after we know diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 7f2fe5889c3..d42d6fb7271 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -166,6 +166,9 @@ INTL_CFLAGS = @INCINTL@ TARGET_SYSTEM_ROOT = @TARGET_SYSTEM_ROOT@ TARGET_SYSTEM_ROOT_DEFINE = @TARGET_SYSTEM_ROOT_DEFINE@ +# Did the user give us a --with-gdb-datadir option? +GDB_DATADIR_PATH = @GDB_DATADIR_PATH@ + # Helper code from gnulib. LIBGNU = gnulib/libgnu.a INCGNU = -I$(srcdir)/gnulib -Ignulib @@ -677,7 +680,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ xml-tdesc.c xml-support.c \ inferior.c gdb_usleep.c \ record.c \ - jit.c + jit.c \ + xml-syscall.c \ LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -746,7 +750,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.h gdb-stabs.h reggroups.h \ annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \ remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \ sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \ -gdb_usleep.h jit.h +gdb_usleep.h jit.h xml-syscall.h # Header files that already have srcdir in them, or which are in objdir. @@ -826,11 +830,17 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ trad-frame.o \ tramp-frame.o \ solib.o solib-null.o \ - prologue-value.o memory-map.o xml-support.o \ + prologue-value.o memory-map.o xml-support.o xml-syscall.o \ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ inferior.o osdata.o gdb_usleep.o record.o \ jit.o +# Definitions for the syscall's XML files and dir +XML_SYSCALLS_DIR = syscalls/ +XML_SYSCALLS_FILES = gdb-syscalls.dtd \ + ppc-linux.xml ppc64-linux.xml \ + i386-linux.xml amd64-linux.xml + TSOBS = inflow.o SUBDIRS = @subdirs@ @@ -864,11 +874,41 @@ generated_files = config.h observer.h observer.inc ada-lex.c \ $(COMPILE) $< $(POSTCOMPILE) -all: gdb$(EXEEXT) $(CONFIG_ALL) +all: gdb$(EXEEXT) $(CONFIG_ALL) xml-syscall-copy @$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=`echo $(SUBDIRS) | sed 's/testsuite//'`" subdir_do .PHONY: all-tui all-tui: $(TUI)$(EXEEXT) +# This is needed for running GDB from the build directory +.PHONY: xml-syscall-copy +xml-syscall-copy: + if [ "`cd $(srcdir) && pwd`" != "`pwd`" ] ; then \ + mkdir -p ./$(XML_SYSCALLS_DIR) ; \ + list='$(XML_SYSCALLS_FILES)' ; \ + for file in $$list ; do \ + f=$(srcdir)/$(XML_SYSCALLS_DIR)/$$file ; \ + if test -f $$f ; then \ + $(INSTALL_DATA) $$f \ + ./$(XML_SYSCALLS_DIR) ; \ + fi ; \ + done ; \ + fi ; + +# This target is responsible for properly installing the syscalls' +# XML files in the system. +.PHONY: xml-syscall-install +xml-syscall-install: + $(SHELL) $(srcdir)/../mkinstalldirs \ + $(DESTDIR)$(GDB_DATADIR_PATH)/$(XML_SYSCALLS_DIR) ; \ + list='$(XML_SYSCALLS_FILES)' ; \ + for file in $$list ; do \ + f=$(srcdir)/$(XML_SYSCALLS_DIR)/$$file ; \ + if test -f $$f ; then \ + $(INSTALL_DATA) $$f \ + $(DESTDIR)$(GDB_DATADIR_PATH)/$(XML_SYSCALLS_DIR) ; \ + fi ; \ + done ; + installcheck: # The check target can not use subdir_do, because subdir_do does not @@ -925,8 +965,11 @@ gdb.z:gdb.1 # source file and doesn't care about rebuilding or just wants to save the # time it takes for make to check that all is up to date. # install-only is intended to address that need. -install: all install-only -install-only: $(CONFIG_INSTALL) +install: all install-only + +# The "install-only" target also installs the syscalls' XML files in +# the system. +install-only: $(CONFIG_INSTALL) xml-syscall-install transformed_name=`t='$(program_transform_name)'; \ echo gdb | sed -e "$$t"` ; \ if test "x$$transformed_name" = x; then \ diff --git a/gdb/NEWS b/gdb/NEWS index 56fdae9f8f8..4fc6dcde137 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -234,6 +234,16 @@ powerpc-linux or powerpc64-linux and the spu-elf targets, using the * New commands (for set/show, see "New options" below) +catch syscall [NAME(S) | NUMBER(S)] + Catch system calls. Arguments, which should be names of system + calls or their numbers, mean catch only those syscalls. Without + arguments, every syscall will be caught. When the inferior issues + any of the specified syscalls, GDB will stop and announce the system + call, both when it is called and when its call returns. This + feature is currently available with a native GDB running on the + Linux Kernel, under the following architectures: x86, x86_64, + PowerPC and PowerPC64. + find [/size-char] [/max-count] start-address, end-address|+search-space-size, val1 [, val2, ...] Search memory for a sequence of bytes. diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index 791e2c4b804..f527b7eab29 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -35,6 +35,10 @@ #include "amd64-tdep.h" #include "solib-svr4.h" +#include "xml-syscall.h" + +/* The syscall's XML filename for i386. */ +#define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml" #include "record.h" #include "linux-record.h" @@ -174,6 +178,28 @@ amd64_linux_sigcontext_addr (struct frame_info *this_frame) } +static LONGEST +amd64_linux_get_syscall_number (struct gdbarch *gdbarch, + ptid_t ptid) +{ + struct regcache *regcache = get_thread_regcache (ptid); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + /* The content of a register. */ + gdb_byte buf[8]; + /* The result. */ + LONGEST ret; + + /* Getting the system call number from the register. + When dealing with x86_64 architecture, this information + is stored at %rax register. */ + regcache_cooked_read (regcache, AMD64_LINUX_ORIG_RAX_REGNUM, buf); + + ret = extract_signed_integer (buf, 8, byte_order); + + return ret; +} + + /* From . */ static int amd64_linux_sc_reg_offset[] = { @@ -1168,6 +1194,11 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_register_type (gdbarch, amd64_linux_register_type); set_gdbarch_register_reggroup_p (gdbarch, amd64_linux_register_reggroup_p); + /* Functions for 'catch syscall'. */ + set_xml_syscall_file_name (XML_SYSCALL_FILENAME_AMD64); + set_gdbarch_get_syscall_number (gdbarch, + amd64_linux_get_syscall_number); + /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 9f50872da6c..811cdfb4fe0 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -60,6 +60,7 @@ #include "wrapper.h" #include "valprint.h" #include "jit.h" +#include "xml-syscall.h" /* readline include files */ #include "readline/readline.h" @@ -204,6 +205,8 @@ static int is_hardware_watchpoint (struct breakpoint *bpt); static void insert_breakpoint_locations (void); +static int syscall_catchpoint_p (struct breakpoint *b); + static void tracepoints_info (char *, int); static void delete_trace_command (char *, int); @@ -4445,6 +4448,7 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch, b->frame_id = null_frame_id; b->forked_inferior_pid = null_ptid; b->exec_pathname = NULL; + b->syscalls_to_be_caught = NULL; b->ops = NULL; b->condition_not_parsed = 0; @@ -4939,7 +4943,266 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops = print_mention_catch_vfork }; -/* Create a new breakpoint of the bp_catchpoint kind and return it. +/* Implement the "insert" breakpoint_ops method for syscall + catchpoints. */ + +static void +insert_catch_syscall (struct breakpoint *b) +{ + struct inferior *inf = current_inferior (); + + ++inf->total_syscalls_count; + if (!b->syscalls_to_be_caught) + ++inf->any_syscall_count; + else + { + int i, iter; + for (i = 0; + VEC_iterate (int, b->syscalls_to_be_caught, i, iter); + i++) + { + int elem; + if (iter >= VEC_length (int, inf->syscalls_counts)) + { + int old_size = VEC_length (int, inf->syscalls_counts); + uintptr_t vec_addr_offset = old_size * ((uintptr_t) sizeof (int)); + uintptr_t vec_addr; + VEC_safe_grow (int, inf->syscalls_counts, iter + 1); + vec_addr = (uintptr_t) VEC_address (int, inf->syscalls_counts) + + vec_addr_offset; + memset ((void *) vec_addr, 0, + (iter + 1 - old_size) * sizeof (int)); + } + elem = VEC_index (int, inf->syscalls_counts, iter); + VEC_replace (int, inf->syscalls_counts, iter, ++elem); + } + } + + target_set_syscall_catchpoint (PIDGET (inferior_ptid), + inf->total_syscalls_count != 0, + inf->any_syscall_count, + VEC_length (int, inf->syscalls_counts), + VEC_address (int, inf->syscalls_counts)); +} + +/* Implement the "remove" breakpoint_ops method for syscall + catchpoints. */ + +static int +remove_catch_syscall (struct breakpoint *b) +{ + struct inferior *inf = current_inferior (); + + --inf->total_syscalls_count; + if (!b->syscalls_to_be_caught) + --inf->any_syscall_count; + else + { + int i, iter; + for (i = 0; + VEC_iterate (int, b->syscalls_to_be_caught, i, iter); + i++) + { + int elem; + if (iter >= VEC_length (int, inf->syscalls_counts)) + /* Shouldn't happen. */ + continue; + elem = VEC_index (int, inf->syscalls_counts, iter); + VEC_replace (int, inf->syscalls_counts, iter, --elem); + } + } + + return target_set_syscall_catchpoint (PIDGET (inferior_ptid), + inf->total_syscalls_count != 0, + inf->any_syscall_count, + VEC_length (int, inf->syscalls_counts), + VEC_address (int, inf->syscalls_counts)); +} + +/* Implement the "breakpoint_hit" breakpoint_ops method for syscall + catchpoints. */ + +static int +breakpoint_hit_catch_syscall (struct breakpoint *b) +{ + /* 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. */ + int syscall_number = 0; + + if (!inferior_has_called_syscall (inferior_ptid, &syscall_number)) + return 0; + + /* Now, checking if the syscall is the same. */ + if (b->syscalls_to_be_caught) + { + int i, iter; + for (i = 0; + VEC_iterate (int, b->syscalls_to_be_caught, i, iter); + i++) + if (syscall_number == iter) + break; + /* Not the same. */ + if (!iter) + return 0; + } + + return 1; +} + +/* Implement the "print_it" breakpoint_ops method for syscall + catchpoints. */ + +static enum print_stop_action +print_it_catch_syscall (struct breakpoint *b) +{ + /* These are 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". */ + ptid_t ptid; + struct target_waitstatus last; + struct syscall s; + struct cleanup *old_chain; + char *syscall_id; + + get_last_target_status (&ptid, &last); + + get_syscall_by_number (last.value.syscall_number, &s); + + annotate_catchpoint (b->number); + + if (s.name == NULL) + syscall_id = xstrprintf ("%d", last.value.syscall_number); + else + syscall_id = xstrprintf ("'%s'", s.name); + + old_chain = make_cleanup (xfree, syscall_id); + + if (last.kind == TARGET_WAITKIND_SYSCALL_ENTRY) + printf_filtered (_("\nCatchpoint %d (call to syscall %s), "), + b->number, syscall_id); + else if (last.kind == TARGET_WAITKIND_SYSCALL_RETURN) + printf_filtered (_("\nCatchpoint %d (returned from syscall %s), "), + b->number, syscall_id); + + do_cleanups (old_chain); + + return PRINT_SRC_AND_LOC; +} + +/* Implement the "print_one" breakpoint_ops method for syscall + catchpoints. */ + +static void +print_one_catch_syscall (struct breakpoint *b, + struct bp_location **last_loc) +{ + struct value_print_options opts; + + get_user_print_options (&opts); + /* 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 (opts.addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + + if (b->syscalls_to_be_caught + && VEC_length (int, b->syscalls_to_be_caught) > 1) + ui_out_text (uiout, "syscalls \""); + else + ui_out_text (uiout, "syscall \""); + + if (b->syscalls_to_be_caught) + { + int i, iter; + char *text = xstrprintf ("%s", ""); + for (i = 0; + VEC_iterate (int, b->syscalls_to_be_caught, i, iter); + i++) + { + char *x = text; + struct syscall s; + get_syscall_by_number (iter, &s); + + if (s.name != NULL) + text = xstrprintf ("%s%s, ", text, s.name); + else + text = xstrprintf ("%s%d, ", text, iter); + + /* We have to xfree the last 'text' (now stored at 'x') + because xstrprintf dinamically allocates new space for it + on every call. */ + xfree (x); + } + /* Remove the last comma. */ + text[strlen (text) - 2] = '\0'; + ui_out_field_string (uiout, "what", text); + } + else + ui_out_field_string (uiout, "what", ""); + ui_out_text (uiout, "\" "); +} + +/* Implement the "print_mention" breakpoint_ops method for syscall + catchpoints. */ + +static void +print_mention_catch_syscall (struct breakpoint *b) +{ + if (b->syscalls_to_be_caught) + { + int i, iter; + + if (VEC_length (int, b->syscalls_to_be_caught) > 1) + printf_filtered (_("Catchpoint %d (syscalls"), b->number); + else + printf_filtered (_("Catchpoint %d (syscall"), b->number); + + for (i = 0; + VEC_iterate (int, b->syscalls_to_be_caught, i, iter); + i++) + { + struct syscall s; + get_syscall_by_number (iter, &s); + + if (s.name) + printf_filtered (" '%s' [%d]", s.name, s.number); + else + printf_filtered (" %d", s.number); + } + printf_filtered (")"); + } + else + printf_filtered (_("Catchpoint %d (any syscall)"), + b->number); +} + +/* The breakpoint_ops structure to be used in syscall catchpoints. */ + +static struct breakpoint_ops catch_syscall_breakpoint_ops = +{ + insert_catch_syscall, + remove_catch_syscall, + breakpoint_hit_catch_syscall, + print_it_catch_syscall, + print_one_catch_syscall, + print_mention_catch_syscall +}; + +/* Returns non-zero if 'b' is a syscall catchpoint. */ + +static int +syscall_catchpoint_p (struct breakpoint *b) +{ + return (b->ops == &catch_syscall_breakpoint_ops); +} + +/* Create a new breakpoint of the bp_catchpoint kind and return it, + but does NOT mention it nor update the global location list. + This is useful if you need to fill more fields in the + struct breakpoint before calling mention. If TEMPFLAG is non-zero, then make the breakpoint temporary. If COND_STRING is not NULL, then store it in the breakpoint. @@ -4947,16 +5210,14 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops = to the catchpoint. */ static struct breakpoint * -create_catchpoint (struct gdbarch *gdbarch, int tempflag, - char *cond_string, struct breakpoint_ops *ops) +create_catchpoint_without_mention (struct gdbarch *gdbarch, int tempflag, + char *cond_string, + struct breakpoint_ops *ops) { struct symtab_and_line sal; struct breakpoint *b; init_sal (&sal); - sal.pc = 0; - sal.symtab = NULL; - sal.line = 0; b = set_raw_breakpoint (gdbarch, sal, bp_catchpoint); set_breakpoint_count (breakpoint_count + 1); @@ -4969,6 +5230,23 @@ create_catchpoint (struct gdbarch *gdbarch, int tempflag, b->disposition = tempflag ? disp_del : disp_donttouch; b->ops = ops; + return b; +} + +/* Create a new breakpoint of the bp_catchpoint kind and return it. + + If TEMPFLAG is non-zero, then make the breakpoint temporary. + If COND_STRING is not NULL, then store it in the breakpoint. + OPS, if not NULL, is the breakpoint_ops structure associated + to the catchpoint. */ + +static struct breakpoint * +create_catchpoint (struct gdbarch *gdbarch, int tempflag, + char *cond_string, struct breakpoint_ops *ops) +{ + struct breakpoint *b = + create_catchpoint_without_mention (gdbarch, tempflag, cond_string, ops); + mention (b); update_global_location_list (1); @@ -5055,6 +5333,22 @@ static struct breakpoint_ops catch_exec_breakpoint_ops = print_mention_catch_exec }; +static void +create_syscall_event_catchpoint (int tempflag, VEC(int) *filter, + struct breakpoint_ops *ops) +{ + struct gdbarch *gdbarch = get_current_arch (); + struct breakpoint *b = + create_catchpoint_without_mention (gdbarch, tempflag, NULL, ops); + + b->syscalls_to_be_caught = filter; + + /* Now, we have to mention the breakpoint and update the global + location list. */ + mention (b); + update_global_location_list (1); +} + static int hw_breakpoint_used_count (void) { @@ -7150,6 +7444,113 @@ catch_ada_exception_command (char *arg, int from_tty, from_tty); } +/* Cleanup function for a syscall filter list. */ +static void +clean_up_filters (void *arg) +{ + VEC(int) *iter = *(VEC(int) **) arg; + VEC_free (int, iter); +} + +/* Splits the argument using space as delimiter. Returns an xmalloc'd + filter list, or NULL if no filtering is required. */ +static VEC(int) * +catch_syscall_split_args (char *arg) +{ + VEC(int) *result = NULL; + struct cleanup *cleanup = make_cleanup (clean_up_filters, &result); + + while (*arg != '\0') + { + int i, syscall_number; + char *endptr; + char cur_name[128]; + struct syscall s; + + /* Skip whitespace. */ + while (isspace (*arg)) + arg++; + + for (i = 0; i < 127 && arg[i] && !isspace (arg[i]); ++i) + cur_name[i] = arg[i]; + cur_name[i] = '\0'; + arg += i; + + /* Check if the user provided a syscall name or a number. */ + syscall_number = (int) strtol (cur_name, &endptr, 0); + if (*endptr == '\0') + { + get_syscall_by_number (syscall_number, &s); + + if (s.name == NULL) + /* We can issue just a warning, but still create the catchpoint. + This is because, even not knowing the syscall name that + this number represents, we can still try to catch the syscall + number. */ + warning (_("The number '%d' does not represent a known syscall."), + syscall_number); + } + else + { + /* We have a name. Let's check if it's valid and convert it + to a number. */ + get_syscall_by_name (cur_name, &s); + + if (s.number == UNKNOWN_SYSCALL) + /* Here we have to issue an error instead of a warning, because + GDB cannot do anything useful if there's no syscall number to + be caught. */ + error (_("Unknown syscall name '%s'."), cur_name); + } + + /* Ok, it's valid. */ + VEC_safe_push (int, result, s.number); + } + + discard_cleanups (cleanup); + return result; +} + +/* Implement the "catch syscall" command. */ + +static void +catch_syscall_command_1 (char *arg, int from_tty, struct cmd_list_element *command) +{ + int tempflag; + VEC(int) *filter; + struct syscall s; + struct gdbarch *gdbarch = get_current_arch (); + + /* Checking if the feature if supported. */ + if (gdbarch_get_syscall_number_p (gdbarch) == 0) + error (_("The feature 'catch syscall' is not supported on \ +this architeture yet.")); + + tempflag = get_cmd_context (command) == CATCH_TEMPORARY; + + ep_skip_leading_whitespace (&arg); + + /* We need to do this first "dummy" translation in order + to get the syscall XML file loaded or, most important, + to display a warning to the user if there's no XML file + for his/her architecture. */ + get_syscall_by_number (0, &s); + + /* The allowed syntax is: + catch syscall + catch syscall [ ... ] + + Let's check if there's a syscall name. */ + + if (arg != NULL) + filter = catch_syscall_split_args (arg); + else + filter = NULL; + + create_syscall_event_catchpoint (tempflag, filter, + &catch_syscall_breakpoint_ops); +} + /* Implement the "catch assert" command. */ static void @@ -7616,6 +8017,7 @@ delete_breakpoint (struct breakpoint *bpt) xfree (bpt->source_file); if (bpt->exec_pathname != NULL) xfree (bpt->exec_pathname); + clean_up_filters (&bpt->syscalls_to_be_caught); /* Be sure no bpstat's are pointing at it after it's been freed. */ /* FIXME, how can we find all bpstat's? @@ -8552,6 +8954,60 @@ single_step_breakpoint_inserted_here_p (CORE_ADDR pc) return 0; } +/* Returns 0 if 'bp' is NOT a syscall catchpoint, + non-zero otherwise. */ +static int +is_syscall_catchpoint_enabled (struct breakpoint *bp) +{ + if (syscall_catchpoint_p (bp) + && bp->enable_state != bp_disabled + && bp->enable_state != bp_call_disabled) + return 1; + else + return 0; +} + +int +catch_syscall_enabled (void) +{ + struct inferior *inf = current_inferior (); + + return inf->total_syscalls_count != 0; +} + +int +catching_syscall_number (int syscall_number) +{ + struct breakpoint *bp; + + ALL_BREAKPOINTS (bp) + if (is_syscall_catchpoint_enabled (bp)) + { + if (bp->syscalls_to_be_caught) + { + int i, iter; + for (i = 0; + VEC_iterate (int, bp->syscalls_to_be_caught, i, iter); + i++) + if (syscall_number == iter) + return 1; + } + else + return 1; + } + + return 0; +} + +/* Complete syscall names. Used by "catch syscall". */ +static char ** +catch_syscall_completer (struct cmd_list_element *cmd, + char *text, char *word) +{ + const char **list = get_syscall_names (); + return (list == NULL) ? NULL : complete_on_enum (list, text, word); +} + /* Tracepoint-specific operations. */ /* Set tracepoint count to NUM. */ @@ -8903,6 +9359,8 @@ static void add_catch_command (char *name, char *docstring, void (*sfunc) (char *args, int from_tty, struct cmd_list_element *command), + char **(*completer) (struct cmd_list_element *cmd, + char *text, char *word), void *user_data_catch, void *user_data_tcatch) { @@ -8912,11 +9370,13 @@ add_catch_command (char *name, char *docstring, &catch_cmdlist); set_cmd_sfunc (command, sfunc); set_cmd_context (command, user_data_catch); + set_cmd_completer (command, completer); command = add_cmd (name, class_breakpoint, NULL, docstring, &tcatch_cmdlist); set_cmd_sfunc (command, sfunc); set_cmd_context (command, user_data_tcatch); + set_cmd_completer (command, completer); } void @@ -9190,36 +9650,53 @@ Set temporary catchpoints to catch events."), Catch an exception, when caught.\n\ With an argument, catch only exceptions with the given name."), catch_catch_command, + NULL, CATCH_PERMANENT, CATCH_TEMPORARY); add_catch_command ("throw", _("\ Catch an exception, when thrown.\n\ With an argument, catch only exceptions with the given name."), catch_throw_command, + NULL, CATCH_PERMANENT, CATCH_TEMPORARY); add_catch_command ("fork", _("Catch calls to fork."), catch_fork_command_1, + NULL, (void *) (uintptr_t) catch_fork_permanent, (void *) (uintptr_t) catch_fork_temporary); add_catch_command ("vfork", _("Catch calls to vfork."), catch_fork_command_1, + NULL, (void *) (uintptr_t) catch_vfork_permanent, (void *) (uintptr_t) catch_vfork_temporary); add_catch_command ("exec", _("Catch calls to exec."), catch_exec_command_1, + NULL, + CATCH_PERMANENT, + CATCH_TEMPORARY); + add_catch_command ("syscall", _("\ +Catch system calls by their names and/or numbers.\n\ +Arguments say which system calls to catch. If no arguments\n\ +are given, every system call will be caught.\n\ +Arguments, if given, should be one or more system call names\n\ +(if your system supports that), or system call numbers."), + catch_syscall_command_1, + catch_syscall_completer, CATCH_PERMANENT, CATCH_TEMPORARY); add_catch_command ("exception", _("\ Catch Ada exceptions, when raised.\n\ With an argument, catch only exceptions with the given name."), catch_ada_exception_command, + NULL, CATCH_PERMANENT, CATCH_TEMPORARY); add_catch_command ("assert", _("\ Catch failed Ada assertions, when raised.\n\ With an argument, catch only exceptions with the given name."), catch_assert_command, + NULL, CATCH_PERMANENT, CATCH_TEMPORARY); diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 70b1398aef6..ba499c6fe04 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -33,7 +33,8 @@ struct block; #define BREAKPOINT_MAX 16 -/* Type of breakpoint. */ + +/* Type of breakpoint. */ /* FIXME In the future, we should fold all other breakpoint-like things into here. This includes: @@ -359,6 +360,9 @@ enum watchpoint_triggered watch_triggered_yes }; +/* This is used to declare the VEC syscalls_to_be_caught. */ +DEF_VEC_I(int); + typedef struct bp_location *bp_location_p; DEF_VEC_P(bp_location_p); @@ -469,6 +473,12 @@ struct breakpoint triggered. */ char *exec_pathname; + /* Syscall numbers used for the 'catch syscall' feature. + If no syscall has been specified for filtering, its value is NULL. + Otherwise, it holds a list of all syscalls to be caught. + The list elements are allocated with xmalloc. */ + VEC(int) *syscalls_to_be_caught; + /* Methods associated with this breakpoint. */ struct breakpoint_ops *ops; @@ -924,6 +934,15 @@ extern int breakpoints_always_inserted_mode (void); 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); + /* Tell a breakpoint to be quiet. */ extern void make_breakpoint_silent (struct breakpoint *); diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 8a9bb7b8ab9..b0e0f6c1677 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2009-09-14 Sergio Durigan Junior + + * gdb.texinfo (Set Catchpoints): Documentation about the catch syscall + feature. + 2009-09-13 Daniel Jacobowitz * gdbint.texinfo (Unwinding the Frame ID): Reference outer_frame_id. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 20273515ab8..80b47893118 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3685,6 +3685,137 @@ A failed Ada assertion. A call to @code{exec}. This is currently only available for HP-UX and @sc{gnu}/Linux. +@item syscall +@itemx syscall @r{[}@var{name} @r{|} @var{number}@r{]} @r{...} +@cindex break on a system call. +A call to or return from a system call, a.k.a.@: @dfn{syscall}. A +syscall is a mechanism for application programs to request a service +from the operating system (OS) or one of the OS system services. +@value{GDBN} can catch some or all of the syscalls issued by the +debuggee, and show the related information for each syscall. If no +argument is specified, calls to and returns from all system calls +will be caught. + +@var{name} can be any system call name that is valid for the +underlying OS. Just what syscalls are valid depends on the OS. On +GNU and Unix systems, you can find the full list of valid syscall +names on @file{/usr/include/asm/unistd.h}. + +@c For MS-Windows, the syscall names and the corresponding numbers +@c can be found, e.g., on this URL: +@c http://www.metasploit.com/users/opcode/syscalls.html +@c but we don't support Windows syscalls yet. + +Normally, @value{GDBN} knows in advance which syscalls are valid for +each OS, so you can use the @value{GDBN} command-line completion +facilities (@pxref{Completion,, command completion}) to list the +available choices. + +You may also specify the system call numerically. A syscall's +number is the value passed to the OS's syscall dispatcher to +identify the requested service. When you specify the syscall by its +name, @value{GDBN} uses its database of syscalls to convert the name +into the corresponding numeric code, but using the number directly +may be useful if @value{GDBN}'s database does not have the complete +list of syscalls on your system (e.g., because @value{GDBN} lags +behind the OS upgrades). + +The example below illustrates how this command works if you don't provide +arguments to it: + +@smallexample +(@value{GDBP}) catch syscall +Catchpoint 1 (syscall) +(@value{GDBP}) r +Starting program: /tmp/catch-syscall + +Catchpoint 1 (call to syscall 'close'), \ + 0xffffe424 in __kernel_vsyscall () +(@value{GDBP}) c +Continuing. + +Catchpoint 1 (returned from syscall 'close'), \ + 0xffffe424 in __kernel_vsyscall () +(@value{GDBP}) +@end smallexample + +Here is an example of catching a system call by name: + +@smallexample +(@value{GDBP}) catch syscall chroot +Catchpoint 1 (syscall 'chroot' [61]) +(@value{GDBP}) r +Starting program: /tmp/catch-syscall + +Catchpoint 1 (call to syscall 'chroot'), \ + 0xffffe424 in __kernel_vsyscall () +(@value{GDBP}) c +Continuing. + +Catchpoint 1 (returned from syscall 'chroot'), \ + 0xffffe424 in __kernel_vsyscall () +(@value{GDBP}) +@end smallexample + +An example of specifying a system call numerically. In the case +below, the syscall number has a corresponding entry in the XML +file, so @value{GDBN} finds its name and prints it: + +@smallexample +(@value{GDBP}) catch syscall 252 +Catchpoint 1 (syscall(s) 'exit_group') +(@value{GDBP}) r +Starting program: /tmp/catch-syscall + +Catchpoint 1 (call to syscall 'exit_group'), \ + 0xffffe424 in __kernel_vsyscall () +(@value{GDBP}) c +Continuing. + +Program exited normally. +(@value{GDBP}) +@end smallexample + +However, there can be situations when there is no corresponding name +in XML file for that syscall number. In this case, @value{GDBN} prints +a warning message saying that it was not able to find the syscall name, +but the catchpoint will be set anyway. See the example below: + +@smallexample +(@value{GDBP}) catch syscall 764 +warning: The number '764' does not represent a known syscall. +Catchpoint 2 (syscall 764) +(@value{GDBP}) +@end smallexample + +If you configure @value{GDBN} using the @samp{--without-expat} option, +it will not be able to display syscall names. Also, if your +architecture does not have an XML file describing its system calls, +you will not be able to see the syscall names. It is important to +notice that these two features are used for accessing the syscall +name database. In either case, you will see a warning like this: + +@smallexample +(@value{GDBP}) catch syscall +warning: Could not open "syscalls/i386-linux.xml" +warning: Could not load the syscall XML file 'syscalls/i386-linux.xml'. +GDB will not be able to display syscall names. +Catchpoint 1 (syscall) +(@value{GDBP}) +@end smallexample + +Of course, the file name will change depending on your architecture and system. + +Still using the example above, you can also try to catch a syscall by its +number. In this case, you would see something like: + +@smallexample +(@value{GDBP}) catch syscall 252 +Catchpoint 1 (syscall(s) 252) +@end smallexample + +Again, in this case @value{GDBN} would not be able to display syscall's names. + @item fork A call to @code{fork}. This is currently only available for HP-UX and @sc{gnu}/Linux. diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index a20df000f3d..e1b9d0d4a31 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -244,6 +244,7 @@ struct gdbarch gdbarch_target_signal_to_host_ftype *target_signal_to_host; gdbarch_get_siginfo_type_ftype *get_siginfo_type; gdbarch_record_special_symbol_ftype *record_special_symbol; + gdbarch_get_syscall_number_ftype *get_syscall_number; int has_global_solist; int has_global_breakpoints; }; @@ -381,6 +382,7 @@ struct gdbarch startup_gdbarch = default_target_signal_to_host, /* target_signal_to_host */ 0, /* get_siginfo_type */ 0, /* record_special_symbol */ + 0, /* get_syscall_number */ 0, /* has_global_solist */ 0, /* has_global_breakpoints */ /* startup_gdbarch() */ @@ -637,6 +639,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of target_signal_to_host, invalid_p == 0 */ /* Skip verify of get_siginfo_type, has predicate */ /* Skip verify of record_special_symbol, has predicate */ + /* Skip verify of get_syscall_number, has predicate */ /* Skip verify of has_global_solist, invalid_p == 0 */ /* Skip verify of has_global_breakpoints, invalid_p == 0 */ buf = ui_file_xstrdup (log, &length); @@ -865,6 +868,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: get_siginfo_type = <%s>\n", host_address_to_string (gdbarch->get_siginfo_type)); + 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 = <%s>\n", + host_address_to_string (gdbarch->get_syscall_number)); fprintf_unfiltered (file, "gdbarch_dump: has_global_breakpoints = %s\n", plongest (gdbarch->has_global_breakpoints)); @@ -3380,6 +3389,30 @@ set_gdbarch_record_special_symbol (struct gdbarch *gdbarch, 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_has_global_solist (struct gdbarch *gdbarch) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 9ffef7e8c84..027541d63e6 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -52,6 +52,7 @@ struct bp_target_info; struct target_desc; struct displaced_step_closure; struct core_regset_section; +struct syscall; /* The architecture associated with the connection to the target. @@ -853,6 +854,15 @@ typedef void (gdbarch_record_special_symbol_ftype) (struct gdbarch *gdbarch, str 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); +/* Function 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); + /* True if the list of shared libraries is one and only for all processes, as opposed to a list of shared libraries per inferior. This usually means that all processes, although may or may not share @@ -870,6 +880,9 @@ extern void set_gdbarch_has_global_solist (struct gdbarch *gdbarch, int has_glob extern int gdbarch_has_global_breakpoints (struct gdbarch *gdbarch); extern void set_gdbarch_has_global_breakpoints (struct gdbarch *gdbarch, int has_global_breakpoints); +/* Definition for an unknown syscall, used basically in error-cases. */ +#define UNKNOWN_SYSCALL (-1) + extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch); diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 491efcec15e..3a6e483bf95 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -724,6 +724,11 @@ M:struct type *:get_siginfo_type:void: # Record architecture-specific information from the symbol table. M:void:record_special_symbol:struct objfile *objfile, asymbol *sym:objfile, sym +# Function for the 'catch syscall' feature. + +# Get architecture-specific system calls information from registers. +M:LONGEST:get_syscall_number:ptid_t ptid:ptid + # True if the list of shared libraries is one and only for all # processes, as opposed to a list of shared libraries per inferior. # This usually means that all processes, although may or may not share @@ -848,6 +853,7 @@ struct bp_target_info; struct target_desc; struct displaced_step_closure; struct core_regset_section; +struct syscall; /* The architecture associated with the connection to the target. @@ -926,6 +932,9 @@ done # close it off cat < 0) + request = PTRACE_SYSCALL; + else + request = PTRACE_CONT; if (step) { diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 748fda05caa..78fe88e2d05 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -37,6 +37,10 @@ #include "symtab.h" #include "arch-utils.h" #include "regset.h" +#include "xml-syscall.h" + +/* The syscall's XML filename for i386. */ +#define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml" #include "record.h" #include "linux-record.h" @@ -411,6 +415,27 @@ i386_linux_intx80_sysenter_record (struct regcache *regcache) } +static LONGEST +i386_linux_get_syscall_number (struct gdbarch *gdbarch, + ptid_t ptid) +{ + struct regcache *regcache = get_thread_regcache (ptid); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + /* The content of a register. */ + gdb_byte buf[4]; + /* The result. */ + LONGEST ret; + + /* Getting the system call number from the register. + When dealing with x86 architecture, this information + is stored at %eax register. */ + regcache_cooked_read (regcache, I386_LINUX_ORIG_EAX_REGNUM, buf); + + ret = extract_signed_integer (buf, 4, byte_order); + + return ret; +} + /* The register sets used in GNU/Linux ELF core-dumps are identical to the register sets in `struct user' that are used for a.out core-dumps. These are also used by ptrace(2). The corresponding @@ -697,6 +722,11 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); + /* Functions for 'catch syscall'. */ + set_xml_syscall_file_name (XML_SYSCALL_FILENAME_I386); + set_gdbarch_get_syscall_number (gdbarch, + i386_linux_get_syscall_number); + set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); } diff --git a/gdb/inf-child.c b/gdb/inf-child.c index 2ac702702a8..07ca814b03f 100644 --- a/gdb/inf-child.c +++ b/gdb/inf-child.c @@ -147,6 +147,15 @@ inf_child_remove_exec_catchpoint (int pid) return 0; } +static int +inf_child_set_syscall_catchpoint (int pid, int needed, int any_count, + int table_size, int *table) +{ + /* This version of Unix doesn't support notification of syscall + events. */ + return 0; +} + static int inf_child_can_run (void) { @@ -190,6 +199,7 @@ inf_child_target (void) t->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_set_syscall_catchpoint = inf_child_set_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; diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c index 89a37a6db9b..736154aac0e 100644 --- a/gdb/inf-ptrace.c +++ b/gdb/inf-ptrace.c @@ -332,13 +332,18 @@ inf_ptrace_resume (struct target_ops *ops, 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 (catch_syscall_enabled () > 0) + request = PT_SYSCALL; + else + request = PT_CONTINUE; + if (step) { /* If this system does not support PT_STEP, a higher level diff --git a/gdb/inferior.h b/gdb/inferior.h index 7312e51c5f5..f1b5d173a7d 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -425,6 +425,20 @@ struct inferior /* Private data used by the target vector implementation. */ struct private_inferior *private; + + /* We keep a count of the number of times the user has requested a + particular syscall to be tracked, and pass this information to the + target. This lets capable targets implement filtering directly. */ + + /* Number of times that "any" syscall is requested. */ + int any_syscall_count; + + /* Count of each system call. */ + VEC(int) *syscalls_counts; + + /* This counts all syscall catch requests, so we can readily determine + if any catching is necessary. */ + int total_syscalls_count; }; /* Create an empty inferior list, or empty the existing one. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index a6ca2e3adfc..1a83a259ec8 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -2042,6 +2042,10 @@ wait_for_inferior (int treat_exec_as_sigtrap) state. */ old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); + if (ecs->ws.kind == TARGET_WAITKIND_SYSCALL_ENTRY + || ecs->ws.kind == TARGET_WAITKIND_SYSCALL_RETURN) + ecs->ws.value.syscall_number = UNKNOWN_SYSCALL; + /* Now figure out what to do with the result of the result. */ handle_inferior_event (ecs); @@ -2378,6 +2382,56 @@ stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id) 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 + processed. */ +static int +deal_with_syscall_event (struct execution_control_state *ecs) +{ + struct regcache *regcache = get_thread_regcache (ecs->ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + int syscall_number = gdbarch_get_syscall_number (gdbarch, + ecs->ptid); + target_last_waitstatus.value.syscall_number = syscall_number; + + if (catch_syscall_enabled () > 0 + && catching_syscall_number (syscall_number) > 0) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n", + syscall_number); + ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP; + + if (!ptid_equal (ecs->ptid, inferior_ptid)) + { + context_switch (ecs->ptid); + reinit_frame_cache (); + } + + stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); + + 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. */ @@ -2698,9 +2752,11 @@ handle_inferior_event (struct execution_control_state *ecs) 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 @@ -2710,9 +2766,10 @@ handle_inferior_event (struct execution_control_state *ecs) 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) @@ -5626,6 +5683,25 @@ inferior_has_execd (ptid_t pid, char **execd_pathname) 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; diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 1308844bf21..98f6347fc9c 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -67,6 +67,10 @@ # endif #endif /* HAVE_PERSONALITY */ +/* To be used when one needs to know wether a + WSTOPSIG (status) is a syscall */ +#define TRAP_IS_SYSCALL (SIGTRAP | 0x80) + /* This comment documents high-level logic of this file. Waiting for events in sync mode @@ -279,6 +283,11 @@ struct simple_pid_list *stopped_pids; static int linux_supports_tracefork_flag = -1; +/* This variable is a tri-state flag: -1 for unknown, 0 if PTRACE_O_TRACESYSGOOD + can not be used, 1 if it can. */ + +static int linux_supports_tracesysgood_flag = -1; + /* If we have PTRACE_O_TRACEFORK, this flag indicates whether we also have PTRACE_O_TRACEVFORKDONE. */ @@ -290,6 +299,9 @@ static int linux_supports_tracevforkdone_flag = -1; linux_nat_wait should behave as if async mode was off. */ static int linux_nat_async_mask_value = 1; +/* Stores the current used ptrace() options. */ +static int current_ptrace_options = 0; + /* The read/write ends of the pipe registered as waitable file in the event loop. */ static int linux_nat_event_pipe[2] = { -1, -1 }; @@ -525,6 +537,43 @@ linux_test_for_tracefork (int original_pid) restore_child_signals_mask (&prev_mask); } +/* Determine if PTRACE_O_TRACESYSGOOD can be used to follow syscalls. + + We try to enable syscall tracing on ORIGINAL_PID. If this fails, + we know that the feature is not available. This may change the tracing + options for ORIGINAL_PID, but we'll be setting them shortly anyway. */ + +static void +linux_test_for_tracesysgood (int original_pid) +{ + int ret; + sigset_t prev_mask; + + /* We don't want those ptrace calls to be interrupted. */ + block_child_signals (&prev_mask); + + linux_supports_tracesysgood_flag = 0; + + ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD); + if (ret != 0) + goto out; + + linux_supports_tracesysgood_flag = 1; +out: + restore_child_signals_mask (&prev_mask); +} + +/* Determine wether we support PTRACE_O_TRACESYSGOOD option available. + This function also sets linux_supports_tracesysgood_flag. */ + +static int +linux_supports_tracesysgood (int pid) +{ + if (linux_supports_tracesysgood_flag == -1) + linux_test_for_tracesysgood (pid); + return linux_supports_tracesysgood_flag; +} + /* Return non-zero iff we have tracefork functionality available. This function also sets linux_supports_tracefork_flag. */ @@ -544,12 +593,27 @@ linux_supports_tracevforkdone (int pid) return linux_supports_tracevforkdone_flag; } +static void +linux_enable_tracesysgood (ptid_t ptid) +{ + int pid = ptid_get_lwp (ptid); + + if (pid == 0) + pid = ptid_get_pid (ptid); + + if (linux_supports_tracesysgood (pid) == 0) + return; + + current_ptrace_options |= PTRACE_O_TRACESYSGOOD; + + ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options); +} + void linux_enable_event_reporting (ptid_t ptid) { int pid = ptid_get_lwp (ptid); - int options; if (pid == 0) pid = ptid_get_pid (ptid); @@ -557,15 +621,16 @@ linux_enable_event_reporting (ptid_t ptid) if (! linux_supports_tracefork (pid)) return; - options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC - | PTRACE_O_TRACECLONE; + current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE; + if (linux_supports_tracevforkdone (pid)) - options |= PTRACE_O_TRACEVFORKDONE; + current_ptrace_options |= PTRACE_O_TRACEVFORKDONE; /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support read-only process state. */ - ptrace (PTRACE_SETOPTIONS, pid, 0, options); + ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options); } static void @@ -573,6 +638,7 @@ linux_child_post_attach (int pid) { linux_enable_event_reporting (pid_to_ptid (pid)); check_for_thread_db (); + linux_enable_tracesysgood (pid_to_ptid (pid)); } static void @@ -580,6 +646,7 @@ linux_child_post_startup_inferior (ptid_t ptid) { linux_enable_event_reporting (ptid); check_for_thread_db (); + linux_enable_tracesysgood (ptid); } static int @@ -810,6 +877,20 @@ linux_child_insert_exec_catchpoint (int pid) error (_("Your system does not support exec catchpoints.")); } +static int +linux_child_set_syscall_catchpoint (int pid, int needed, int any_count, + int table_size, int *table) +{ + if (! linux_supports_tracesysgood (pid)) + error (_("Your system does not support syscall catchpoints.")); + /* On GNU/Linux, we ignore the arguments. It means that we only + enable the syscall catchpoints, but do not disable them. + + Also, we do not use the `table' information because we do not + filter system calls here. We let GDB do the logic for us. */ + return 0; +} + /* On GNU/Linux there are no real LWP's. The closest thing to LWP's are processes sharing the same VM space. A multi-threaded process is basically a group of such processes. However, such a grouping @@ -1982,6 +2063,47 @@ linux_handle_extended_wait (struct lwp_info *lp, int status, return 0; } + /* Used for 'catch syscall' feature. */ + if (WSTOPSIG (status) == TRAP_IS_SYSCALL) + { + if (catch_syscall_enabled () == 0) + ourstatus->kind = TARGET_WAITKIND_IGNORE; + else + { + struct regcache *regcache = get_thread_regcache (lp->ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + ourstatus->value.syscall_number = + (int) gdbarch_get_syscall_number (gdbarch, lp->ptid); + + /* If we are catching this specific syscall number, then we + should update the target_status to reflect which event + has occurred. But if this syscall is not to be caught, + then we can safely mark the event as a SYSCALL_RETURN. + + This is particularly needed if: + + - We are catching any syscalls, or + - We are catching the syscall "exit" + + In this case, as the syscall "exit" *doesn't* return, + then GDB would be confused because it would mark the last + syscall event as a SYSCALL_ENTRY. After that, if we re-ran the + inferior GDB will think that the first syscall event is + the opposite of a SYSCALL_ENTRY, which is the SYSCALL_RETURN. + Therefore, GDB would report inverted syscall events. */ + if (catching_syscall_number (ourstatus->value.syscall_number)) + ourstatus->kind = + (lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY) ? + TARGET_WAITKIND_SYSCALL_RETURN : TARGET_WAITKIND_SYSCALL_ENTRY; + else + ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN; + + lp->syscall_state = ourstatus->kind; + } + return 0; + } + internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event); } @@ -2580,11 +2702,16 @@ linux_nat_filter_event (int lwpid, int status, int options) } /* Save the trap's siginfo in case we need it later. */ - if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP) + if (WIFSTOPPED (status) + && (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == TRAP_IS_SYSCALL)) save_siginfo (lp); - /* Handle GNU/Linux's extended waitstatus for trace events. */ - if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0) + /* Handle GNU/Linux's extended waitstatus for trace events. + It is necessary to check if WSTOPSIG is signaling that + the inferior is entering/exiting a system call. */ + if (WIFSTOPPED (status) + && ((WSTOPSIG (status) == TRAP_IS_SYSCALL) + || (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0))) { if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, @@ -4510,6 +4637,7 @@ linux_target_install_ops (struct target_ops *t) t->to_insert_fork_catchpoint = linux_child_insert_fork_catchpoint; t->to_insert_vfork_catchpoint = linux_child_insert_vfork_catchpoint; t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint; + t->to_set_syscall_catchpoint = linux_child_set_syscall_catchpoint; t->to_pid_to_exec_file = linux_child_pid_to_exec_file; t->to_post_startup_inferior = linux_child_post_startup_inferior; t->to_post_attach = linux_child_post_attach; diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index d1ed6fcbf13..eae74f3afd1 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -70,6 +70,13 @@ struct lwp_info or to a local variable in lin_lwp_wait. */ struct target_waitstatus waitstatus; + /* Signal wether we are in a SYSCALL_ENTRY or + in a SYSCALL_RETURN event. + Values: + - TARGET_WAITKIND_SYSCALL_ENTRY + - TARGET_WAITKIND_SYSCALL_RETURN */ + int syscall_state; + /* Next LWP in list. */ struct lwp_info *next; }; diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c index f0f802c6479..d8211edfd87 100644 --- a/gdb/ppc-linux-tdep.c +++ b/gdb/ppc-linux-tdep.c @@ -47,6 +47,7 @@ #include "exceptions.h" #include "arch-utils.h" #include "spu-tdep.h" +#include "xml-syscall.h" #include "features/rs6000/powerpc-32l.c" #include "features/rs6000/powerpc-altivec32l.c" @@ -64,6 +65,9 @@ #include "features/rs6000/powerpc-isa205-vsx64l.c" #include "features/rs6000/powerpc-e500l.c" +/* The syscall's XML filename for PPC and PPC64. */ +#define XML_SYSCALL_FILENAME_PPC "syscalls/ppc-linux.xml" +#define XML_SYSCALL_FILENAME_PPC64 "syscalls/ppc64-linux.xml" /* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint in much the same fashion as memory_remove_breakpoint in mem-break.c, @@ -1066,6 +1070,39 @@ ppc_linux_trap_reg_p (struct gdbarch *gdbarch) && register_size (gdbarch, PPC_TRAP_REGNUM) > 0; } +/* Return the current system call's number present in the + r0 register. When the function fails, it returns -1. */ +static LONGEST +ppc_linux_get_syscall_number (struct gdbarch *gdbarch, + ptid_t ptid) +{ + struct regcache *regcache = get_thread_regcache (ptid); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct cleanup *cleanbuf; + /* The content of a register */ + gdb_byte *buf; + /* The result */ + LONGEST ret; + + /* Make sure we're in a 32- or 64-bit machine */ + gdb_assert (tdep->wordsize == 4 || tdep->wordsize == 8); + + buf = (gdb_byte *) xmalloc (tdep->wordsize * sizeof (gdb_byte)); + + cleanbuf = make_cleanup (xfree, buf); + + /* Getting the system call number from the register. + When dealing with PowerPC architecture, this information + is stored at 0th register. */ + regcache_cooked_read (regcache, tdep->ppc_gp0_regnum, buf); + + ret = extract_signed_integer (buf, tdep->wordsize, byte_order); + do_cleanups (cleanbuf); + + return ret; +} + static void ppc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) { @@ -1435,6 +1472,9 @@ ppc_linux_init_abi (struct gdbarch_info info, /* Handle inferior calls during interrupted system calls. */ set_gdbarch_write_pc (gdbarch, ppc_linux_write_pc); + /* Get the syscall number from the arch's register. */ + set_gdbarch_get_syscall_number (gdbarch, ppc_linux_get_syscall_number); + if (tdep->wordsize == 4) { /* Until November 2001, gcc did not comply with the 32 bit SysV @@ -1454,6 +1494,9 @@ ppc_linux_init_abi (struct gdbarch_info info, set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_ilp32_fetch_link_map_offsets); + /* Setting the correct XML syscall filename. */ + set_xml_syscall_file_name (XML_SYSCALL_FILENAME_PPC); + /* Trampolines. */ tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sigaction_tramp_frame); tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sighandler_tramp_frame); @@ -1477,6 +1520,9 @@ ppc_linux_init_abi (struct gdbarch_info info, set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_lp64_fetch_link_map_offsets); + /* Setting the correct XML syscall filename. */ + set_xml_syscall_file_name (XML_SYSCALL_FILENAME_PPC64); + /* Trampolines. */ tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sigaction_tramp_frame); tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sighandler_tramp_frame); diff --git a/gdb/target.c b/gdb/target.c index cb955bda0fe..ed7ccdc9997 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -647,6 +647,7 @@ update_current_target (void) /* Do not inherit to_follow_fork. */ INHERIT (to_insert_exec_catchpoint, t); INHERIT (to_remove_exec_catchpoint, t); + INHERIT (to_set_syscall_catchpoint, t); INHERIT (to_has_exited, t); /* Do not inherit to_mourn_inferiour. */ INHERIT (to_can_run, t); @@ -789,6 +790,9 @@ update_current_target (void) de_fault (to_remove_exec_catchpoint, (int (*) (int)) tcomplain); + de_fault (to_set_syscall_catchpoint, + (int (*) (int, int, int, int, int *)) + tcomplain); de_fault (to_has_exited, (int (*) (int, int, int *)) return_zero); @@ -2866,9 +2870,9 @@ target_waitstatus_to_string (const struct target_waitstatus *ws) case TARGET_WAITKIND_EXECD: return xstrprintf ("%sexecd", kind_str); case TARGET_WAITKIND_SYSCALL_ENTRY: - return xstrprintf ("%ssyscall-entry", kind_str); + return xstrprintf ("%sentered syscall", kind_str); case TARGET_WAITKIND_SYSCALL_RETURN: - return xstrprintf ("%ssyscall-return", kind_str); + return xstrprintf ("%sexited syscall", kind_str); case TARGET_WAITKIND_SPURIOUS: return xstrprintf ("%sspurious", kind_str); case TARGET_WAITKIND_IGNORE: diff --git a/gdb/target.h b/gdb/target.h index c210fea3b1b..b1cb8523b56 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -142,14 +142,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 number. */ union { int integer; enum target_signal sig; ptid_t related_pid; char *execd_pathname; - int syscall_id; + int syscall_number; } value; }; @@ -161,6 +162,21 @@ struct target_waitstatus event. */ #define TARGET_WNOHANG 1 +/* The structure below stores information about a system call. + It is basically used in the "catch syscall" command, and in + every function that gives information about a system call. + + It's also good to mention that its fields represent everything + that we currently know about a syscall in GDB. */ +struct syscall + { + /* The syscall number. */ + int number; + + /* The syscall name. */ + const char *name; + }; + /* Return a pretty printed form of target_waitstatus. Space for the result is malloc'd, caller must free. */ extern char *target_waitstatus_to_string (const struct target_waitstatus *); @@ -406,6 +422,7 @@ 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_set_syscall_catchpoint) (int, int, int, int, int *); int (*to_has_exited) (int, int, int *); void (*to_mourn_inferior) (struct target_ops *); int (*to_can_run) (void); @@ -748,6 +765,8 @@ extern int inferior_has_vforked (ptid_t pid, ptid_t *child_pid); extern int inferior_has_execd (ptid_t pid, char **execd_pathname); +extern int inferior_has_called_syscall (ptid_t pid, int *syscall_number); + /* Print a line about the current target. */ #define target_files_info() \ @@ -900,6 +919,27 @@ int target_follow_fork (int follow_child); #define target_remove_exec_catchpoint(pid) \ (*current_target.to_remove_exec_catchpoint) (pid) +/* Syscall catch. + + NEEDED is nonzero if any syscall catch (of any kind) is requested. + If NEEDED is zero, it means the target can disable the mechanism to + catch system calls because there are no more catchpoints of this type. + + ANY_COUNT is nonzero if a generic (filter-less) syscall catch is + being requested. In this case, both TABLE_SIZE and TABLE should + be ignored. + + TABLE_SIZE is the number of elements in TABLE. It only matters if + ANY_COUNT is zero. + + TABLE is an array of ints, indexed by syscall number. An element in + this array is nonzero if that syscall should be caught. This argument + only matters if ANY_COUNT is zero. */ + +#define target_set_syscall_catchpoint(pid, needed, any_count, table_size, table) \ + (*current_target.to_set_syscall_catchpoint) (pid, needed, any_count, \ + table_size, table) + /* Returns TRUE if PID has exited. And, also sets EXIT_STATUS to the exit code of PID, if any. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ccf5677e129..a80d9a252ca 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2009-09-14 Sergio Durigan Junior + + * Makefile.in: Inclusion of catch-syscall object. + * gdb.base/catch-syscall.c: New file. + * gdb.base/catch-syscall.exp: New file. + 2009-09-12 Michael Snyder * gdb.reverse/step-reverse.exp: Explicitly check for targets diff --git a/gdb/testsuite/gdb.base/Makefile.in b/gdb/testsuite/gdb.base/Makefile.in index 9f382db8b5a..12db5212889 100644 --- a/gdb/testsuite/gdb.base/Makefile.in +++ b/gdb/testsuite/gdb.base/Makefile.in @@ -12,7 +12,7 @@ EXECUTABLES = all-types annota1 bitfields break \ scope section_command setshow setvar shmain sigall signals \ solib solib_sl so-impl-ld so-indr-cl \ step-line step-test structs structs2 \ - twice-tmp varargs vforked-prog watchpoint whatis + twice-tmp varargs vforked-prog watchpoint whatis catch-syscall MISCELLANEOUS = coremmap.data ../foobar.baz \ shr1.sl shr2.sl solib_sl.sl solib1.sl solib2.sl diff --git a/gdb/xml-support.c b/gdb/xml-support.c index 937c6c31772..ad20d3bf954 100644 --- a/gdb/xml-support.c +++ b/gdb/xml-support.c @@ -1036,6 +1036,66 @@ obstack_xml_printf (struct obstack *obstack, const char *format, ...) va_end (ap); } +char * +xml_fetch_content_from_file (const char *filename, void *baton) +{ + const char *dirname = baton; + FILE *file; + struct cleanup *back_to; + char *text; + size_t len, offset; + + if (dirname && *dirname) + { + char *fullname = concat (dirname, "/", filename, (char *) NULL); + if (fullname == NULL) + nomem (0); + file = fopen (fullname, FOPEN_RT); + xfree (fullname); + } + else + file = fopen (filename, FOPEN_RT); + + if (file == NULL) + return NULL; + + back_to = make_cleanup_fclose (file); + + /* Read in the whole file, one chunk at a time. */ + len = 4096; + offset = 0; + text = xmalloc (len); + make_cleanup (free_current_contents, &text); + while (1) + { + size_t bytes_read; + + /* Continue reading where the last read left off. Leave at least + one byte so that we can NUL-terminate the result. */ + bytes_read = fread (text + offset, 1, len - offset - 1, file); + if (ferror (file)) + { + warning (_("Read error from \"%s\""), filename); + do_cleanups (back_to); + return NULL; + } + + offset += bytes_read; + + if (feof (file)) + break; + + len = len * 2; + text = xrealloc (text, len); + } + + fclose (file); + discard_cleanups (back_to); + + text[offset] = '\0'; + return text; +} + void _initialize_xml_support (void); void diff --git a/gdb/xml-support.h b/gdb/xml-support.h index d6105f73413..135263d5d52 100644 --- a/gdb/xml-support.h +++ b/gdb/xml-support.h @@ -240,4 +240,10 @@ extern void obstack_xml_printf (struct obstack *obstack, const char *format, ...) ATTRIBUTE_PRINTF_2; +/* Open FILENAME, read all its text into memory, close it, and return + the text. If something goes wrong, return NULL and warn. */ + +extern char *xml_fetch_content_from_file (const char *filename, + void *baton); + #endif diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c index 4fa78439bef..102049f2966 100644 --- a/gdb/xml-tdesc.c +++ b/gdb/xml-tdesc.c @@ -426,69 +426,6 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher, #endif /* HAVE_LIBEXPAT */ -/* Open FILENAME, read all its text into memory, close it, and return - the text. If something goes wrong, return NULL and warn. */ - -static char * -fetch_xml_from_file (const char *filename, void *baton) -{ - const char *dirname = baton; - FILE *file; - struct cleanup *back_to; - char *text; - size_t len, offset; - - if (dirname && *dirname) - { - char *fullname = concat (dirname, "/", filename, (char *) NULL); - if (fullname == NULL) - nomem (0); - file = fopen (fullname, FOPEN_RT); - xfree (fullname); - } - else - file = fopen (filename, FOPEN_RT); - - if (file == NULL) - return NULL; - - back_to = make_cleanup_fclose (file); - - /* Read in the whole file, one chunk at a time. */ - len = 4096; - offset = 0; - text = xmalloc (len); - make_cleanup (free_current_contents, &text); - while (1) - { - size_t bytes_read; - - /* Continue reading where the last read left off. Leave at least - one byte so that we can NUL-terminate the result. */ - bytes_read = fread (text + offset, 1, len - offset - 1, file); - if (ferror (file)) - { - warning (_("Read error from \"%s\""), filename); - do_cleanups (back_to); - return NULL; - } - - offset += bytes_read; - - if (feof (file)) - break; - - len = len * 2; - text = xrealloc (text, len); - } - - fclose (file); - discard_cleanups (back_to); - - text[offset] = '\0'; - return text; -} - /* Read an XML target description from FILENAME. Parse it, and return the parsed description. */ @@ -500,7 +437,7 @@ file_read_description_xml (const char *filename) struct cleanup *back_to; char *dirname; - tdesc_str = fetch_xml_from_file (filename, NULL); + tdesc_str = xml_fetch_content_from_file (filename, NULL); if (tdesc_str == NULL) { warning (_("Could not open \"%s\""), filename); @@ -513,7 +450,7 @@ file_read_description_xml (const char *filename) if (dirname != NULL) make_cleanup (xfree, dirname); - tdesc = tdesc_parse_xml (tdesc_str, fetch_xml_from_file, dirname); + tdesc = tdesc_parse_xml (tdesc_str, xml_fetch_content_from_file, dirname); do_cleanups (back_to); return tdesc;