* [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
@ 2009-09-04 19:01 Sérgio Durigan Júnior
2009-09-05 7:20 ` Eli Zaretskii
2009-09-05 7:24 ` Eli Zaretskii
0 siblings, 2 replies; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-04 19:01 UTC (permalink / raw)
To: GDB Patches
[-- Attachment #1: Type: text/plain, Size: 5038 bytes --]
Hi,
Here is the part of the patch where I've modified the source-code (next parts
will touch XML support and Build system/testcase/documentation). This is the
only part of the whole patch that changed, in order to implement the x86_64
support. Also, I moved three global variables from gdb/breakpoint.c to struct
inferior (I had to do it in order to fix an annoying bug).
Regards,
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
gdb/ChangeLog:
2009-09-04 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
* 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.
[-- Attachment #2: catch-syscall-source.patch --]
[-- Type: text/x-patch, Size: 58268 bytes --]
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index dcfdb38..259e8ec 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)
}
\f
+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 <asm/sigcontext.h>. */
static int amd64_linux_sc_reg_offset[] =
{
@@ -1411,6 +1437,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 9f50872..7395b31 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", "<any syscall>");
+ 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; 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, 10);
+ 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 <name | number> [<name | number> ... <name | number>]
+
+ 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,56 @@ 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.\n\
+The program can take one or more arguments (syscalls names\n\
+and/or numbers), in which case it will catch the syscalls\n\
+provided. It can also take no arguments, in which case it\n\
+will catch every syscall.\n\
+The argument(s) should be the syscall name(s) (if your system\n\
+has support for it), or the syscall number(s). If you need\n\
+more information, please refer to GDB manual."),
+ 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 70b1398..ba499c6 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,8 @@ struct block;
#define BREAKPOINT_MAX 16
\f
-/* 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/gdbarch.c b/gdb/gdbarch.c
index a20df00..e1b9d0d 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);
@@ -866,6 +869,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *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));
fprintf_unfiltered (file,
@@ -3381,6 +3390,30 @@ set_gdbarch_record_special_symbol (struct gdbarch *gdbarch,
}
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)
{
gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 9ffef7e..027541d 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 491efce..3a6e483 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 <<EOF
+/* 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/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 991b27f..fe848ff 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -752,7 +752,12 @@ i386_linux_resume (struct target_ops *ops,
{
int pid = PIDGET (ptid);
- int request = PTRACE_CONT;
+ int request;
+
+ if (catch_syscall_enabled () > 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 ecc634f..f661642 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"
@@ -391,6 +395,27 @@ i386_linux_intx80_sysenter_record (struct regcache *regcache)
}
\f
+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
@@ -677,6 +702,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 2ac7027..07ca814 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -148,6 +148,15 @@ inf_child_remove_exec_catchpoint (int pid)
}
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)
{
return 1;
@@ -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 89a37a6..736154a 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 7312e51..f1b5d17 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 c907635..713810b 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)
@@ -5614,6 +5671,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 1308844..eb2a4b9 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);
+}
+
\f
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,16 @@ 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."));
+ /* We ignore the arguments. */
+ 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 +2059,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 +2698,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 +4633,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 d1ed6fc..eae74f3 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 f0f802c..d8211ed 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 d828332..d77a0e6 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);
@@ -2865,9 +2869,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 c210fea..78bcb14 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,17 @@ 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. ANY_COUNT is nonzero if a generic
+ (filter-less) syscall catch is being requested. TABLE is an array
+ of ints, indexed by syscall number. An element in this array is
+ nonzero if that syscall should be caught. TABLE_SIZE is the number
+ of elements in TABLE. */
+
+#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/xml-support.c b/gdb/xml-support.c
index 937c6c3..ad20d3b 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 d6105f7..135263d 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 4fa7843..102049f 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 */
\f
-/* 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;
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-04 19:01 [RFC 1/3] catch syscall -- try 6 -- Source-code modifications Sérgio Durigan Júnior
@ 2009-09-05 7:20 ` Eli Zaretskii
2009-09-05 7:24 ` Eli Zaretskii
1 sibling, 0 replies; 12+ messages in thread
From: Eli Zaretskii @ 2009-09-05 7:20 UTC (permalink / raw)
To: Sérgio Durigan Júnior; +Cc: gdb-patches
> From: Sérgio_Durigan_Júnior <sergiodj@linux.vnet.ibm.com>
> Date: Fri, 4 Sep 2009 16:00:47 -0300
>
> + add_catch_command ("syscall", _("\
> +Catch system calls.\n\
> +The program can take one or more arguments (syscalls names\n\
You meant "The command", right?
> +and/or numbers), in which case it will catch the syscalls\n\
> +provided. It can also take no arguments, in which case it\n\
> +will catch every syscall.\n\
> +The argument(s) should be the syscall name(s) (if your system\n\
> +has support for it), or the syscall number(s). If you need\n\
> +more information, please refer to GDB manual."),
This is okay, but too wordy at times. Suggest the following variant
instead:
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."),
My only other comment about the code part is that it would be nice to
have somewhere a description of what a target should do to add support
for this feature.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-04 19:01 [RFC 1/3] catch syscall -- try 6 -- Source-code modifications Sérgio Durigan Júnior
2009-09-05 7:20 ` Eli Zaretskii
@ 2009-09-05 7:24 ` Eli Zaretskii
2009-09-10 22:42 ` Sérgio Durigan Júnior
1 sibling, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2009-09-05 7:24 UTC (permalink / raw)
To: Sérgio Durigan Júnior; +Cc: gdb-patches
> +/* Syscall catch. NEEDED is nonzero if any syscall catch (of any
> + kind) is requested. ANY_COUNT is nonzero if a generic
> + (filter-less) syscall catch is being requested. TABLE is an array
> + of ints, indexed by syscall number. An element in this array is
> + nonzero if that syscall should be caught. TABLE_SIZE is the number
> + of elements in TABLE. */
> +
> +#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)
> +
Could you perhaps expand the comment a bit more? For example, what
should the target do if NEEDED is zero? Also, I understand that
ANY_COUNT nonzero means TABLE should be ignored, is that right?
IOW, imagine that someone is to implement this method, and try to give
any information necessary to write the code.
Thanks.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-05 7:24 ` Eli Zaretskii
@ 2009-09-10 22:42 ` Sérgio Durigan Júnior
2009-09-11 8:09 ` Eli Zaretskii
0 siblings, 1 reply; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-10 22:42 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 1320 bytes --]
Hi Eli,
On Saturday 05 September 2009, Eli Zaretskii wrote:
> > +/* Syscall catch. NEEDED is nonzero if any syscall catch (of any
> > + kind) is requested. ANY_COUNT is nonzero if a generic
> > + (filter-less) syscall catch is being requested. TABLE is an array
> > + of ints, indexed by syscall number. An element in this array is
> > + nonzero if that syscall should be caught. TABLE_SIZE is the number
> > + of elements in TABLE. */
> > +
> > +#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)
> > +
>
> Could you perhaps expand the comment a bit more? For example, what
> should the target do if NEEDED is zero? Also, I understand that
> ANY_COUNT nonzero means TABLE should be ignored, is that right?
>
> IOW, imagine that someone is to implement this method, and try to give
> any information necessary to write the code.
I tried to be more descriptive about this piece of code. I also addressed
your other message, fixing some wrong comments. What do you think now?
Thank you for reviewing this,
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
[-- Attachment #2: catch-syscall-source.patch --]
[-- Type: text/x-patch, Size: 58694 bytes --]
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 791e2c4..f527b7e 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)
}
\f
+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 <asm/sigcontext.h>. */
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 9f50872..5799935 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", "<any syscall>");
+ 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; 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, 10);
+ 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 <name | number> [<name | number> ... <name | number>]
+
+ 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 70b1398..ba499c6 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,8 @@ struct block;
#define BREAKPOINT_MAX 16
\f
-/* 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/gdbarch.c b/gdb/gdbarch.c
index a20df00..e1b9d0d 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);
@@ -866,6 +869,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *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));
fprintf_unfiltered (file,
@@ -3381,6 +3390,30 @@ set_gdbarch_record_special_symbol (struct gdbarch *gdbarch,
}
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)
{
gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 9ffef7e..027541d 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 491efce..3a6e483 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 <<EOF
+/* 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/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 991b27f..fe848ff 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -752,7 +752,12 @@ i386_linux_resume (struct target_ops *ops,
{
int pid = PIDGET (ptid);
- int request = PTRACE_CONT;
+ int request;
+
+ if (catch_syscall_enabled () > 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 748fda0..78fe88e 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)
}
\f
+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 2ac7027..07ca814 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -148,6 +148,15 @@ inf_child_remove_exec_catchpoint (int pid)
}
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)
{
return 1;
@@ -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 89a37a6..736154a 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 7312e51..f1b5d17 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 c907635..713810b 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)
@@ -5614,6 +5671,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 1308844..98f6347 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);
+}
+
\f
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 d1ed6fc..eae74f3 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 f0f802c..d8211ed 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 a662eca..73226e9 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);
@@ -2865,9 +2869,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 c210fea..b1cb852 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/xml-support.c b/gdb/xml-support.c
index 937c6c3..ad20d3b 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 d6105f7..135263d 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 4fa7843..102049f 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 */
\f
-/* 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;
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-10 22:42 ` Sérgio Durigan Júnior
@ 2009-09-11 8:09 ` Eli Zaretskii
2009-09-12 0:28 ` Sérgio Durigan Júnior
0 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2009-09-11 8:09 UTC (permalink / raw)
To: Sérgio Durigan Júnior; +Cc: gdb-patches
> From: Sérgio_Durigan_Júnior <sergiodj@linux.vnet.ibm.com>
> Date: Thu, 10 Sep 2009 19:42:26 -0300
> Cc: gdb-patches@sourceware.org
>
> > Could you perhaps expand the comment a bit more? For example, what
> > should the target do if NEEDED is zero? Also, I understand that
> > ANY_COUNT nonzero means TABLE should be ignored, is that right?
> >
> > IOW, imagine that someone is to implement this method, and try to give
> > any information necessary to write the code.
>
> I tried to be more descriptive about this piece of code. I also addressed
> your other message, fixing some wrong comments. What do you think now?
It's fine, now the API is clear. Thanks.
I have one more small request:
> + /* Check if the user provided a syscall name or a number. */
> + syscall_number = (int) strtol (cur_name, &endptr, 10);
This forces the user to specify the system call numbers in decimal.
However, sometimes it might be more convenient to use hex. For
example, I'm planning to add support for this to the DJGPP port, where
the various software interrupts and their functions are known to
people by their hex numbers, e.g. Interrupt 0x21 function 0x2a is the
DOS system call to get the system clock date. Asking the users to
translate those into decimal would be a nuisance.
So can we use zero instead of 10 for the last argument of this call to
strtol?
Finally, a minor nit:
> + 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; arg[i] && !isspace (arg[i]); ++i)
> + cur_name[i] = arg[i];
> + cur_name[i] = '\0';
> + arg += i;
The last loop does not take care not to overrun the 128-character
limit that cur_name[] imposes on the length of syscall names, and will
happily smash the stack if GDB is fed a very long string.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-11 8:09 ` Eli Zaretskii
@ 2009-09-12 0:28 ` Sérgio Durigan Júnior
2009-09-12 8:20 ` Eli Zaretskii
0 siblings, 1 reply; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-12 0:28 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 1844 bytes --]
Hi Eli,
On Friday 11 September 2009, Eli Zaretskii wrote:
> I have one more small request:
> > + /* Check if the user provided a syscall name or a number. */
> > + syscall_number = (int) strtol (cur_name, &endptr, 10);
>
> This forces the user to specify the system call numbers in decimal.
> However, sometimes it might be more convenient to use hex. For
> example, I'm planning to add support for this to the DJGPP port, where
> the various software interrupts and their functions are known to
> people by their hex numbers, e.g. Interrupt 0x21 function 0x2a is the
> DOS system call to get the system clock date. Asking the users to
> translate those into decimal would be a nuisance.
>
> So can we use zero instead of 10 for the last argument of this call to
> strtol?
Sure, I should have used zero since the start. Sorry about that, it's fixed
(and tested) now.
> Finally, a minor nit:
> > + 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; arg[i] && !isspace (arg[i]); ++i)
> > + cur_name[i] = arg[i];
> > + cur_name[i] = '\0';
> > + arg += i;
>
> The last loop does not take care not to overrun the 128-character
> limit that cur_name[] imposes on the length of syscall names, and will
> happily smash the stack if GDB is fed a very long string.
Ops, sorry about that too :-)... Thank you for pointing this nit, I've fixed
it too (and tested, of course).
What about the patch now? Ok to check in?
Regards,
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
[-- Attachment #2: catch-syscall-source.patch --]
[-- Type: text/x-patch, Size: 58550 bytes --]
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 791e2c4..f527b7e 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)
}
\f
+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 <asm/sigcontext.h>. */
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 9f50872..811cdfb 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", "<any syscall>");
+ 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 <name | number> [<name | number> ... <name | number>]
+
+ 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 70b1398..ba499c6 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,8 @@ struct block;
#define BREAKPOINT_MAX 16
\f
-/* 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/gdbarch.c b/gdb/gdbarch.c
index a20df00..e1b9d0d 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);
@@ -866,6 +869,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *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));
fprintf_unfiltered (file,
@@ -3381,6 +3390,30 @@ set_gdbarch_record_special_symbol (struct gdbarch *gdbarch,
}
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)
{
gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 9ffef7e..027541d 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 491efce..3a6e483 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 <<EOF
+/* 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/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 991b27f..fe848ff 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -752,7 +752,12 @@ i386_linux_resume (struct target_ops *ops,
{
int pid = PIDGET (ptid);
- int request = PTRACE_CONT;
+ int request;
+
+ if (catch_syscall_enabled () > 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 748fda0..78fe88e 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)
}
\f
+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 2ac7027..07ca814 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -148,6 +148,15 @@ inf_child_remove_exec_catchpoint (int pid)
}
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)
{
return 1;
@@ -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 89a37a6..736154a 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 7312e51..f1b5d17 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 c907635..713810b 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)
@@ -5614,6 +5671,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 1308844..98f6347 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);
+}
+
\f
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 d1ed6fc..eae74f3 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 f0f802c..d8211ed 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 a662eca..73226e9 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);
@@ -2865,9 +2869,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 c210fea..b1cb852 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/xml-support.c b/gdb/xml-support.c
index 937c6c3..ad20d3b 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 d6105f7..135263d 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 4fa7843..102049f 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 */
\f
-/* 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;
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-12 0:28 ` Sérgio Durigan Júnior
@ 2009-09-12 8:20 ` Eli Zaretskii
2009-09-12 17:15 ` Sérgio Durigan Júnior
0 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2009-09-12 8:20 UTC (permalink / raw)
To: Sérgio Durigan Júnior; +Cc: gdb-patches
> From: Sérgio_Durigan_Júnior <sergiodj@linux.vnet.ibm.com>
> Date: Fri, 11 Sep 2009 21:27:41 -0300
> Cc: gdb-patches@sourceware.org
>
> On Friday 11 September 2009, Eli Zaretskii wrote:
> > I have one more small request:
> > > + /* Check if the user provided a syscall name or a number. */
> > > + syscall_number = (int) strtol (cur_name, &endptr, 10);
> >
> > This forces the user to specify the system call numbers in decimal.
> > However, sometimes it might be more convenient to use hex. For
> > example, I'm planning to add support for this to the DJGPP port, where
> > the various software interrupts and their functions are known to
> > people by their hex numbers, e.g. Interrupt 0x21 function 0x2a is the
> > DOS system call to get the system clock date. Asking the users to
> > translate those into decimal would be a nuisance.
> >
> > So can we use zero instead of 10 for the last argument of this call to
> > strtol?
>
> Sure, I should have used zero since the start. Sorry about that, it's fixed
> (and tested) now.
>
> > Finally, a minor nit:
> > > + 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; arg[i] && !isspace (arg[i]); ++i)
> > > + cur_name[i] = arg[i];
> > > + cur_name[i] = '\0';
> > > + arg += i;
> >
> > The last loop does not take care not to overrun the 128-character
> > limit that cur_name[] imposes on the length of syscall names, and will
> > happily smash the stack if GDB is fed a very long string.
>
> Ops, sorry about that too :-)... Thank you for pointing this nit, I've fixed
> it too (and tested, of course).
Thanks, I am happy now.
> What about the patch now? Ok to check in?
I think everybody approved it, right? How about waiting one more day
and then checking it in?
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-12 8:20 ` Eli Zaretskii
@ 2009-09-12 17:15 ` Sérgio Durigan Júnior
2009-09-13 17:18 ` Sérgio Durigan Júnior
0 siblings, 1 reply; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-12 17:15 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On Saturday 12 September 2009, Eli Zaretskii wrote:
> > What about the patch now? Ok to check in?
>
> I think everybody approved it, right? How about waiting one more day
> and then checking it in?
Sounds like a plan :-). Thank you.
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-12 17:15 ` Sérgio Durigan Júnior
@ 2009-09-13 17:18 ` Sérgio Durigan Júnior
2009-09-15 3:55 ` Sérgio Durigan Júnior
0 siblings, 1 reply; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-13 17:18 UTC (permalink / raw)
To: gdb-patches; +Cc: Eli Zaretskii
Hello,
On Saturday 12 September 2009, Sérgio Durigan Júnior wrote:
> On Saturday 12 September 2009, Eli Zaretskii wrote:
> > > What about the patch now? Ok to check in?
> >
> > I think everybody approved it, right? How about waiting one more day
> > and then checking it in?
>
> Sounds like a plan :-). Thank you.
FYI, I decided to wait until tomorrow (Monday) night, because I suspect that
most of the GMs are not seeing any e-mails this weekend. Anyway, if nobody
complains about my patch, I intend to check it in tomorrow (finally).
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-13 17:18 ` Sérgio Durigan Júnior
@ 2009-09-15 3:55 ` Sérgio Durigan Júnior
2009-09-15 4:13 ` Joel Brobecker
0 siblings, 1 reply; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-15 3:55 UTC (permalink / raw)
To: gdb-patches; +Cc: Eli Zaretskii
Hello,
On Sunday 13 September 2009, Sérgio Durigan Júnior wrote:
> Hello,
>
> On Saturday 12 September 2009, Sérgio Durigan Júnior wrote:
> > On Saturday 12 September 2009, Eli Zaretskii wrote:
> > > > What about the patch now? Ok to check in?
> > >
> > > I think everybody approved it, right? How about waiting one more day
> > > and then checking it in?
> >
> > Sounds like a plan :-). Thank you.
>
> FYI, I decided to wait until tomorrow (Monday) night, because I suspect
> that most of the GMs are not seeing any e-mails this weekend. Anyway, if
> nobody complains about my patch, I intend to check it in tomorrow
> (finally).
The patch finally is commited, but...
Unfortunately, during the commit process, I made some stupid mistakes here
with my local copy of the repository, and had to do 2 or 3 extra commits in
order to include everything needed to implement the catch syscall. Things are
now working as expected (I tested here), but I'm afraid I didn't do a good job
at CVS :-(. Should I do anything to fix this error? I am really, really
sorry about that :-/. At least, GDB now has the catch syscall command, heh...
Regards,
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-15 3:55 ` Sérgio Durigan Júnior
@ 2009-09-15 4:13 ` Joel Brobecker
2009-09-15 4:30 ` Sérgio Durigan Júnior
0 siblings, 1 reply; 12+ messages in thread
From: Joel Brobecker @ 2009-09-15 4:13 UTC (permalink / raw)
To: S?rgio Durigan J?nior; +Cc: gdb-patches, Eli Zaretskii
> Unfortunately, during the commit process, I made some stupid mistakes
> here with my local copy of the repository, and had to do 2 or 3 extra
> commits [...]
No problem at all, as long as the difference between the source before
you committed the change and after is equal to the patch that you
submitted. So, I would not worry about this.
--
Joel
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications
2009-09-15 4:13 ` Joel Brobecker
@ 2009-09-15 4:30 ` Sérgio Durigan Júnior
0 siblings, 0 replies; 12+ messages in thread
From: Sérgio Durigan Júnior @ 2009-09-15 4:30 UTC (permalink / raw)
To: gdb-patches; +Cc: Joel Brobecker, Eli Zaretskii
Hi Joel,
On Tuesday 15 September 2009, Joel Brobecker wrote:
> > Unfortunately, during the commit process, I made some stupid mistakes
> > here with my local copy of the repository, and had to do 2 or 3 extra
> > commits [...]
>
> No problem at all, as long as the difference between the source before
> you committed the change and after is equal to the patch that you
> submitted. So, I would not worry about this.
Great, it is equal so I will not worry. Thanks!
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2009-09-15 4:30 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-04 19:01 [RFC 1/3] catch syscall -- try 6 -- Source-code modifications Sérgio Durigan Júnior
2009-09-05 7:20 ` Eli Zaretskii
2009-09-05 7:24 ` Eli Zaretskii
2009-09-10 22:42 ` Sérgio Durigan Júnior
2009-09-11 8:09 ` Eli Zaretskii
2009-09-12 0:28 ` Sérgio Durigan Júnior
2009-09-12 8:20 ` Eli Zaretskii
2009-09-12 17:15 ` Sérgio Durigan Júnior
2009-09-13 17:18 ` Sérgio Durigan Júnior
2009-09-15 3:55 ` Sérgio Durigan Júnior
2009-09-15 4:13 ` Joel Brobecker
2009-09-15 4:30 ` Sérgio Durigan Júnior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox