This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1/4] 'catch syscall' feature -- Architecture-independent part


This is the architecture independent part of the patch. The ChangeLog is
inlined, and the patch itself is attached.

Regards,

-- 
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil

gdb/ChangeLog:

2008-11-04  Sergio Durigan Junior  <sergiodj@linux.vnet.ibm.com>

	* breakpoint.c (clear_syscall_catchpoints_info): New function.
	(print_it_typical): Handle the case of the breakpoint at the
	entrypoint.
	(bpstat_what): Add the entry breakpoint case.
	(print_one_breakpoint_location): Add the entry breakpoint.
	(allocate_bp_location): Likewise.
	(set_raw_breakpoint_without_location): New.  Used by the
	catch syscall feature because it needs to mention the
	catchpoint after some few adjustments.
	(create_entry_breakpoint): New.
	(insert_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 struct.
	(syscall_catchpoint_p): New.
	(create_catchpoint): Modified in order to use
	create_catchpoint_without_mention.
	(create_syscall_event_catchpoint): New.
	(mention): Add the entry breakpoint.
	(catch_syscall_split_args): New.
	(catch_syscall_command_1): New.
	(delete_command): Add the entry breakpoint case.
	(breakpoint_re_set_one): Likewise.
	(is_syscall_catchpoint_enabled): New.
	(catch_syscall_enabled): New.
	(catching_syscall_number): New.
	* breakpoint.h (enum bptype): Add the entry breakpoint case.
	(struct breakpoint): Add field 'syscall_number' (used to know
	which syscall triggered the catchpoint) and
	'syscall_to_be_caught' (used to know which syscall we are trying
	to catch).
	(enum bpstat_what_main_action): Add BPSTAT_WHAT_ENTRY_BREAKPOINT,
	to identify whether we hit an entry breakpoint.
	(catch_syscall_enabled): New.
	(catching_syscall_number): New.
	(create_entry_breakpoint): New.
	* completer.c: Include gdbarch.h file.
	(catch_syscall_completer): New.
	* completer.h (catch_syscall_completer): New.
	* defs.h (gdb_datadir): New variable.
	* gdbarch.c: Regenerated.
	* gdbarch.h: Regenerated.
	* gdbarch.sh: Add syscall catchpoint functions.
	(get_syscall_number): New.
	(syscall_name_from_number): new.
	(syscall_number_from_name): New.
	(get_syscalls_names): New.
	* gdbthread.h (struct thread_info): Add field 'syscall_state',
	used to know if we are calling or returning from a syscall.
	* inf-child.c (inf_child_insert_syscall_catchpoint): New.
	(inf_child_remove_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.
	* infcmd.c (run_command_1): Clean syscall catchpoint info on every
	run.
	* infrun.c (resume): Add syscall catchpoint and entry breakpoint.
	(deal_with_syscall_event): New.
	(handle_inferior_event): Add syscall entry/return events.  Also,
	add entry breakpoint event.
	(inferior_has_called_syscall): New.
	* main.c: Add gdb_datadir variable to store the current GDB datadir.
	(captured_main): Add the GDB datadir relocatable handler.
	* maint.c: Create the "maintanence set gdb_datadir" c
	* target.c (update_current_target): Update/copy functions related to
	syscall catchpoint and entry breakpoint.
	(debug_to_wait): Add syscall catchpoint entry/return events.
	* target.h (struct target_waitstatus): Add syscall number.
	(struct target_ops): Add ops for syscall catchpoint and entry
	breakpoint.
	(inferior_has_called_syscall): New.
	(target_passed_by_entrypoint): New.
	(target_insert_syscall_catchpoint): New.
	(target_remove_syscall_catchpoint): New.
	(target_enable_tracesysgood): New.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 912d7ea..f3bea56 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -192,6 +192,12 @@ static int is_hardware_watchpoint (struct breakpoint *bpt);
 
 static void insert_breakpoint_locations (void);
 
+static int syscall_catchpoint_p (struct breakpoint *b);
+
+/* The maximum number of arguments the user can provide to
+   the 'catch syscall' command.  */
+#define MAX_CATCH_SYSCALL_ARGS 10
+
 static const char *
 bpdisp_text (enum bpdisp disp)
 {
@@ -395,6 +401,18 @@ set_breakpoint_count (int num)
 		   value_from_longest (builtin_type_int32, (LONGEST) num));
 }
 
+/* Used in run_command to reset syscall catchpoints fields.  */
+
+void
+clear_syscall_catchpoints_info (void)
+{
+  struct breakpoint *b;
+
+  ALL_BREAKPOINTS (b)
+    if (syscall_catchpoint_p (b))
+      b->syscall_number = UNKNOWN_SYSCALL;
+}
+
 /* Used in run_command to zero the hit count when a new run starts. */
 
 void
@@ -2315,6 +2333,14 @@ print_it_typical (bpstat bs)
       return PRINT_NOTHING;
       break;
 
+    case bp_entry_breakpoint:
+       /* Not sure how we will get here. 
+	 GDB should not stop for these breakpoints.  */
+      internal_error (__FILE__, __LINE__,
+                      _("Entry Breakpoint: gdb should not stop!\n"));
+      return PRINT_NOTHING;
+      break;    
+
     case bp_overlay_event:
       /* By analogy with the thread event, GDB should not stop for these. */
       printf_filtered (_("Overlay Event Breakpoint: gdb should not stop!\n"));
@@ -3165,6 +3191,9 @@ bpstat_what (bpstat bs)
       /* We caught a shared library event.  */
       catch_shlib_event,
 
+      /* We are in a entry breakpoint.  */
+      entry_breakpoint,
+
       /* This is just used to count how many enums there are.  */
       class_last
     };
@@ -3181,6 +3210,7 @@ bpstat_what (bpstat bs)
 #define sr BPSTAT_WHAT_STEP_RESUME
 #define shl BPSTAT_WHAT_CHECK_SHLIBS
 #define shlr BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK
+#define entrybp BPSTAT_WHAT_ENTRY_BREAKPOINT
 
 /* "Can't happen."  Might want to print an error message.
    abort() is not out of the question, but chances are GDB is just
@@ -3225,30 +3255,33 @@ bpstat_what (bpstat bs)
     table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
   {
   /*                              old action */
-  /*       kc    ss    sn    sgl    slr   clr   sr   shl   shlr
+  /*       kc    ss    sn    sgl    slr   clr   sr   shl   shlr   entrybp
    */
 /*no_effect */
-    {kc, ss, sn, sgl, slr, clr, sr, shl, shlr},
+    {kc, ss, sn, sgl, slr, clr, sr, shl, shlr, shlr},
 /*wp_silent */
-    {ss, ss, sn, ss, ss, ss, sr, shl, shlr},
+    {ss, ss, sn, ss, ss, ss, sr, shl, shlr, shlr},
 /*wp_noisy */
-    {sn, sn, sn, sn, sn, sn, sr, shl, shlr},
+    {sn, sn, sn, sn, sn, sn, sr, shl, shlr, shlr},
 /*bp_nostop */
-    {sgl, ss, sn, sgl, slr, slr, sr, shl, shlr},
+    {sgl, ss, sn, sgl, slr, slr, sr, shl, shlr, shlr},
 /*bp_silent */
-    {ss, ss, sn, ss, ss, ss, sr, shl, shlr},
+    {ss, ss, sn, ss, ss, ss, sr, shl, shlr, shlr},
 /*bp_noisy */
-    {sn, sn, sn, sn, sn, sn, sr, shl, shlr},
+    {sn, sn, sn, sn, sn, sn, sr, shl, shlr, shlr},
 /*long_jump */
-    {slr, ss, sn, slr, slr, err, sr, shl, shlr},
+    {slr, ss, sn, slr, slr, err, sr, shl, shlr, shlr},
 /*long_resume */
-    {clr, ss, sn, err, err, err, sr, shl, shlr},
+    {clr, ss, sn, err, err, err, sr, shl, shlr, shlr},
 /*step_resume */
-    {sr, sr, sr, sr, sr, sr, sr, sr, sr},
+    {sr, sr, sr, sr, sr, sr, sr, sr, sr, sr},
 /*shlib */
-    {shl, shl, shl, shl, shl, shl, sr, shl, shlr},
+    {shl, shl, shl, shl, shl, shl, sr, shl, shlr, shl},
 /*catch_shlib */
-    {shlr, shlr, shlr, shlr, shlr, shlr, sr, shlr, shlr}
+    {shlr, shlr, shlr, shlr, shlr, shlr, sr, shlr, shlr, shlr},
+/* entry_breakpoint */
+    {entrybp, entrybp, entrybp, entrybp, entrybp, entrybp, sr, entrybp,
+      entrybp, entrybp}
   };
 
 #undef kc
@@ -3262,6 +3295,7 @@ bpstat_what (bpstat bs)
 #undef ts
 #undef shl
 #undef shlr
+#undef entrybp
   enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
   struct bpstat_what retval;
 
@@ -3365,6 +3399,12 @@ bpstat_what (bpstat bs)
 	  bs_class = bp_silent;
 	  retval.call_dummy = 1;
 	  break;
+        case bp_entry_breakpoint:
+          if (bs->stop)
+            bs_class = entry_breakpoint;
+          else
+            bs_class = no_effect;
+          break;
 	}
       current_action = table[(int) bs_class][(int) current_action];
     }
@@ -3526,7 +3566,8 @@ print_one_breakpoint_location (struct breakpoint *b,
     {bp_overlay_event, "overlay events"},
     {bp_catchpoint, "catchpoint"},
     {bp_catch_load, "catch load"},
-    {bp_catch_unload, "catch unload"}
+    {bp_catch_unload, "catch unload"},
+    {bp_entry_breakpoint, "entry breakpoint"}
   };
   
   static char bpenables[] = "nynny";
@@ -3675,6 +3716,7 @@ print_one_breakpoint_location (struct breakpoint *b,
       case bp_shlib_event:
       case bp_thread_event:
       case bp_overlay_event:
+      case bp_entry_breakpoint:
 	if (opts.addressprint)
 	  {
 	    annotate_field (4);
@@ -4248,6 +4290,7 @@ allocate_bp_location (struct breakpoint *bpt, enum bptype bp_type)
     case bp_watchpoint_scope:
     case bp_call_dummy:
     case bp_shlib_event:
+    case bp_entry_breakpoint:
     case bp_thread_event:
     case bp_overlay_event:
     case bp_catch_load:
@@ -4309,6 +4352,8 @@ set_raw_breakpoint_without_location (enum bptype bptype)
   b->triggered_dll_pathname = NULL;
   b->forked_inferior_pid = null_ptid;
   b->exec_pathname = NULL;
+  b->syscall_to_be_caught = CATCHING_ANY_SYSCALL;
+  b->syscall_number = UNKNOWN_SYSCALL;
   b->ops = NULL;
   b->condition_not_parsed = 0;
 
@@ -4531,6 +4576,31 @@ disable_overlay_breakpoints (void)
     }
 }
 
+int
+create_entry_breakpoint ()
+{
+  CORE_ADDR taddr, entry_addr;
+  struct breakpoint *b;
+
+  taddr = entry_point_address ();
+  /* Make certain that the address points at real code, and not a
+     function descriptor.  */
+  entry_addr = gdbarch_convert_from_func_ptr_addr (current_gdbarch, taddr,
+                                                   &current_target);
+
+  /* Setting the breakpoint.  */
+  b = create_internal_breakpoint (entry_addr, bp_entry_breakpoint);
+
+  b->enable_state = bp_enabled;
+  b->disposition = disp_del;
+  /* addr_string has to be used or breakpoint_re_set will delete me.  */
+  b->addr_string = xstrprintf ("AT_ENTRY (0x%s)", paddr (entry_addr));
+
+  update_global_location_list (1);
+
+  return 1;
+}
+
 struct breakpoint *
 create_thread_event_breakpoint (CORE_ADDR address)
 {
@@ -4828,7 +4898,155 @@ 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)
+{
+  target_insert_syscall_catchpoint (PIDGET (inferior_ptid));
+}
+
+/* Implement the "remove" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static int
+remove_catch_syscall (struct breakpoint *b)
+{
+  return target_remove_syscall_catchpoint (PIDGET (inferior_ptid));
+}
+
+/* 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->syscall_to_be_caught != CATCHING_ANY_SYSCALL
+      && b->syscall_to_be_caught != syscall_number)
+    /* Not the same.  */
+    return 0;
+
+  /* It's the same syscall.  We can update the breakpoint struct
+     with the correct information.  */
+  b->syscall_number = syscall_number;
+
+  return 1;
+}
+
+/* Implement the "print_it" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static enum print_stop_action
+print_it_catch_syscall (struct breakpoint *b)
+{
+  /* This is needed because we want to know in which state a
+     syscall is.  It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
+     or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we
+     must print "called syscall" or "returned from syscall".  */
+  struct thread_info *th_info = find_thread_pid (inferior_ptid);
+  const char *syscall_name =
+    gdbarch_syscall_name_from_number (current_gdbarch, b->syscall_number);
+
+  annotate_catchpoint (b->number);
+  printf_filtered (_("\nCatchpoint %d ("), b->number);
+
+  if (th_info->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY)
+    printf_filtered (_("calling "));
+  else
+    printf_filtered (_("returned from "));
+
+  printf_filtered (_("syscall "));
+
+  if (syscall_name == NULL)
+    printf_filtered (_("%d"), b->syscall_number);
+  else
+    printf_filtered (_("'%s ()'"), syscall_name);
+
+  printf_filtered (_("), "));
+
+  return PRINT_SRC_AND_LOC;
+}
+
+/* Implement the "print_one" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static void
+print_one_catch_syscall (struct breakpoint *b, CORE_ADDR *last_addr)
+{
+  struct value_print_options opts;
+  const char *syscall_name =
+    gdbarch_syscall_name_from_number (current_gdbarch, b->syscall_number);
+
+  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);
+  ui_out_text (uiout, "syscall \"");
+  if (b->syscall_number != UNKNOWN_SYSCALL)
+    {
+      if (syscall_name != NULL)
+        ui_out_field_string (uiout, "what", syscall_name);
+      else
+        ui_out_field_int (uiout, "what", b->syscall_number);
+    }
+  else
+    ui_out_field_string (uiout, "what", "<unknown 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->syscall_to_be_caught != CATCHING_ANY_SYSCALL)
+    printf_filtered (_("Catchpoint %d (syscall '%s ()')"),
+                     b->number,
+                     gdbarch_syscall_name_from_number (current_gdbarch,
+                                                   b->syscall_to_be_caught));
+  else
+    printf_filtered (_("Catchpoint %d (syscall)"),
+                     b->number);
+}
+
+/* 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.
@@ -4836,16 +5054,13 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
    to the catchpoint.  */
 
 static struct breakpoint *
-create_catchpoint (int tempflag, char *cond_string,
-                   struct breakpoint_ops *ops)
+create_catchpoint_without_mention (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 (sal, bp_catchpoint);
   set_breakpoint_count (breakpoint_count + 1);
@@ -4859,6 +5074,23 @@ create_catchpoint (int tempflag, char *cond_string,
   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 (int tempflag, char *cond_string,
+                   struct breakpoint_ops *ops)
+{
+  struct breakpoint *b =
+    create_catchpoint_without_mention (tempflag, cond_string, ops);
+
   mention (b);
   update_global_location_list (1);
 
@@ -4943,6 +5175,23 @@ static struct breakpoint_ops catch_exec_breakpoint_ops =
   print_mention_catch_exec
 };
 
+static void
+create_syscall_event_catchpoint (int tempflag, int syscall_number,
+                                 struct breakpoint_ops *ops)
+{
+  struct breakpoint *b =
+    create_catchpoint_without_mention (tempflag, NULL, ops);
+
+  b->syscall_to_be_caught = syscall_number;
+  /* We still don't know the syscall that will be caught :-).  */
+  b->syscall_number = UNKNOWN_SYSCALL;
+
+  /* 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)
 {
@@ -5164,6 +5413,7 @@ mention (struct breakpoint *b)
       case bp_shlib_event:
       case bp_thread_event:
       case bp_overlay_event:
+      case bp_entry_breakpoint:
 	break;
       }
 
@@ -6921,6 +7171,107 @@ catch_ada_exception_command (char *arg, int from_tty,
                                    from_tty);
 }
 
+/* Splits the argument using space as delimiter.
+   Returns the number of args.  */
+static int
+catch_syscall_split_args (char *arg, int *syscalls_numbers)
+{
+  int nargs = 0, i;
+  char *arg_p = arg;
+  char cur_name[128];
+  char c;
+
+  while (*arg_p != '\0')
+    {
+      int out = 0;
+      int syscall_number;
+      memset ((void *) cur_name, '\0', 128 * sizeof (char));
+
+      /* Skipping white-spaces.  */
+      while (isspace (*arg_p))
+        arg_p++;
+
+      for (i = 0; out == 0; i++)
+        {
+          c = *arg_p;
+          cur_name[i] = c;
+          if (isspace (c) || c == '\0')
+            {
+              out = 1;
+              cur_name[i] = '\0';
+            }
+          else
+            arg_p++;
+        }
+
+      /* Checking if the user provided a syscall name or a number.  */
+      if (isdigit (cur_name[0]))
+        {
+          /* We have a number.  Let's check if it's valid.  */
+          syscall_number = atoi (cur_name);
+
+          if (gdbarch_syscall_name_from_number (current_gdbarch,
+                                                syscall_number) == NULL)
+            error (_("The number '%d' does not represent a valid syscall."),
+                   syscall_number);
+        }
+      else
+        {
+          /* We have a name.  Let's check if it's valid and convert it
+             to a number.  */
+          syscall_number =
+            gdbarch_syscall_number_from_name (current_gdbarch, cur_name);
+
+          if (syscall_number == UNKNOWN_SYSCALL)
+            error (_("Invalid syscall name '%s'."), cur_name);
+        }
+      /* Ok, it's valid.  */
+      syscalls_numbers[nargs++] = syscall_number;
+    }
+
+    return nargs;
+}
+
+/* Implement the "catch syscall" command.  */
+
+static void
+catch_syscall_command_1 (char *arg, int from_tty, struct cmd_list_element *command)
+{
+  int tempflag;
+
+  /* Checking if the feature if supported.  */
+  if (gdbarch_get_syscall_number_p (current_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);
+
+  /* The allowed syntax is:
+     catch syscall
+     catch syscall <name | number> [<name | number> ... <name | number>]
+
+     The maximum number of arguments this command can take
+     is define by MAX_CATCH_SYSCALL_ARGS.
+
+     Let's check if there's a syscall name.  */
+
+  if (arg != NULL)
+    {
+      int syscalls_numbers[MAX_CATCH_SYSCALL_ARGS];
+      int nargs = catch_syscall_split_args (arg, syscalls_numbers);
+      int i;
+
+      for (i = 0; i < nargs; i++)
+        create_syscall_event_catchpoint (tempflag, syscalls_numbers[i],
+                                         &catch_syscall_breakpoint_ops);
+    }
+  else
+    create_syscall_event_catchpoint (tempflag, CATCHING_ANY_SYSCALL,
+                                     &catch_syscall_breakpoint_ops);
+}
+
 /* Implement the "catch assert" command.  */
 
 static void
@@ -7441,6 +7792,7 @@ delete_command (char *arg, int from_tty)
 	    b->type != bp_shlib_event &&
 	    b->type != bp_thread_event &&
 	    b->type != bp_overlay_event &&
+            b->type != bp_entry_breakpoint &&
 	    b->number >= 0)
 	  {
 	    breaks_to_delete = 1;
@@ -7458,6 +7810,7 @@ delete_command (char *arg, int from_tty)
 		b->type != bp_shlib_event &&
 		b->type != bp_thread_event &&
 		b->type != bp_overlay_event &&
+                b->type != bp_entry_breakpoint &&
 		b->number >= 0)
 	      delete_breakpoint (b);
 	  }
@@ -7766,6 +8119,9 @@ breakpoint_re_set_one (void *bint)
 	 Once it is set up, we do not want to touch it.  */
     case bp_thread_event:
 
+      /* Same for this one */
+    case bp_entry_breakpoint:
+
       /* Keep temporary breakpoints, which can be encountered when we step
          over a dlopen call and SOLIB_ADD is resetting the breakpoints.
          Otherwise these should have been blown away via the cleanup chain
@@ -8329,6 +8685,45 @@ 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 breakpoint *bp;
+
+  ALL_BREAKPOINTS (bp)
+    if (is_syscall_catchpoint_enabled (bp))
+      return 1;
+
+  return 0;
+}
+
+int
+catching_syscall_number (int syscall_number)
+{
+  struct breakpoint *bp;
+
+  ALL_BREAKPOINTS (bp)
+    if (is_syscall_catchpoint_enabled (bp))
+      if (bp->syscall_to_be_caught == syscall_number
+          || bp->syscall_to_be_caught == CATCHING_ANY_SYSCALL)
+        return 1;
+
+  return 0;
+}
+
 
 /* This help string is used for the break, hbreak, tbreak and thbreak commands.
    It is defined as a macro to prevent duplication.
@@ -8361,6 +8756,7 @@ static void
 add_catch_command (char *name, char *docstring,
 		   void (*sfunc) (char *args, int from_tty,
 				  struct cmd_list_element *command),
+                   char ** (*completion_function) (char *text, char *word),
 		   void *user_data_catch,
 		   void *user_data_tcatch)
 {
@@ -8370,11 +8766,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, completion_function);
 
   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, completion_function);
 }
 
 void
@@ -8649,48 +9047,64 @@ 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 calls to syscalls.\n\
+With an argument, catch only calls of that syscall."),
+                         catch_syscall_command_1,
+                         catch_syscall_completer,
+                         CATCH_PERMANENT,
+                         CATCH_TEMPORARY);
   add_catch_command ("load", _("\
 Catch library loads.\n\
 With an argument, catch only loads of that library."),
 		     catch_load_command_1,
+                     NULL,
 		     CATCH_PERMANENT,
 		     CATCH_TEMPORARY);
   add_catch_command ("unload", _("\
 Catch library unloads.\n\
 With an argument, catch only unloads of that library."),
 		     catch_unload_command_1,
+                     NULL,
 		     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 a77b1b4..caecba9 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,12 @@ struct block;
 
 #define	BREAKPOINT_MAX	16
 
-/* Type of breakpoint. */
+
+/* A number to represent wether we are catching any syscalls.  */
+
+#define CATCHING_ANY_SYSCALL (-1)
+
+/* Type of breakpoint.  */
 /* FIXME In the future, we should fold all other breakpoint-like things into
    here.  This includes:
 
@@ -119,6 +124,10 @@ enum bptype
     /* These breakpoints are used to implement the "catch unload" command
        on platforms whose dynamic linkers support such functionality.  */
     bp_catch_unload,
+
+    /* This type is used to signal an internal breakpoint located at
+       the AT_ENTRY address.  */
+    bp_entry_breakpoint,
   };
 
 /* States of enablement of breakpoint. */
@@ -460,6 +469,21 @@ struct breakpoint
        triggered.  */
     char *exec_pathname;
 
+    /* Syscall number used for the 'catch syscall' feature.
+       If no syscall has been called, its value is UNKNOWN_SYSCALL.
+       Otherwise, it holds the system call number in the target.
+
+       This field is only valid immediately after this catchpoint has
+       triggered.  */
+    int syscall_number;
+
+    /* This field is used when we are "filtering" the syscalls
+       (i.e., when the user types "catch syscall <SYSCALL_NAME>".
+
+       It stores the syscall number in case we are in the "filter mode",
+       or CATCHING_ANY_SYSCALL otherwise.  */
+    int syscall_to_be_caught;
+
     /* Methods associated with this breakpoint.  */
     struct breakpoint_ops *ops;
 
@@ -541,6 +565,10 @@ enum bpstat_what_main_action
        resume out of the dynamic linker's callback, stop and print.  */
     BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK,
 
+    /* This internal breakpoint is used syscall catchpoints only after the
+       shell and the dynamic linker have already ran.  */
+    BPSTAT_WHAT_ENTRY_BREAKPOINT,
+
     /* This is just used to keep track of how many enums there are.  */
     BPSTAT_WHAT_LAST
   };
@@ -810,6 +838,8 @@ extern void enable_watchpoints_after_interactive_call_stop (void);
 extern enum command_control_type commands_from_control_command
   (char *arg, struct command_line *cmd);
 
+extern void clear_syscall_catchpoints_info (void);
+
 extern void clear_breakpoint_hit_counts (void);
 
 extern int get_number (char **);
@@ -889,6 +919,27 @@ 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);
+
+/* Function used to set an internal breakpoint at the AT_ENTRY
+   (a.k.a. the entry point of the inferior).
+
+   This is currently needed for us to know when to start setting
+   up catchpoints for syscalls in the inferior.  If we don't do that,
+   then we would set a "catch syscall" too early, which would
+   catch syscalls from ld.so and/or libc (and we don't want that).
+
+   Returns zero if there was an error setting this breakpoint,
+   or 1 if everything went OK.  */
+extern int create_entry_breakpoint (void);
+
 /* Tell a breakpoint to be quiet.  */
 extern void make_breakpoint_silent (struct breakpoint *);
 
diff --git a/gdb/completer.c b/gdb/completer.c
index e7ee817..84a93a8 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -22,6 +22,7 @@
 #include "expression.h"
 #include "filenames.h"		/* For DOSish file names.  */
 #include "language.h"
+#include "gdbarch.h" /* For current_gdbarch.  */
 
 #include "cli/cli-decode.h"
 
@@ -712,6 +713,15 @@ command_completer (char *text, char *word)
   return complete_line_internal (word, text, strlen (text), 1);
 }
 
+/* Complete syscalls names.  Used by "catch syscall".  */
+char **
+catch_syscall_completer (char *text, char *word)
+{
+  const char **list =
+    gdbarch_get_syscalls_names (current_gdbarch);
+  return (list == NULL) ? NULL : complete_on_enum (list, text, word);
+}
+
 /* Generate completions one by one for the completer.  Each time we are
    called return another potential completion to the caller.
    line_completion just completes on commands or passes the buck to the
diff --git a/gdb/completer.h b/gdb/completer.h
index 318594e..2e10136 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -31,6 +31,8 @@ extern char **location_completer (char *, char *);
 
 extern char **command_completer (char *, char *);
 
+extern char **catch_syscall_completer (char *text, char *word);
+
 extern char *get_gdb_completer_quote_characters (void);
 
 /* Exported to linespec.c */
diff --git a/gdb/defs.h b/gdb/defs.h
index 8d50f8a..9284a72 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -151,6 +151,9 @@ extern int dbx_commands;
 /* System root path, used to find libraries etc.  */
 extern char *gdb_sysroot;
 
+/* GDB datadir, used to store data files.  */
+extern char *gdb_datadir;
+
 /* Search path for separate debug files.  */
 extern char *debug_file_directory;
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index dd6ad7f..3ffcaf7 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -240,6 +240,10 @@ struct gdbarch
   gdbarch_target_signal_from_host_ftype *target_signal_from_host;
   gdbarch_target_signal_to_host_ftype *target_signal_to_host;
   gdbarch_record_special_symbol_ftype *record_special_symbol;
+  gdbarch_get_syscall_number_ftype *get_syscall_number;
+  gdbarch_syscall_name_from_number_ftype *syscall_name_from_number;
+  gdbarch_syscall_number_from_name_ftype *syscall_number_from_name;
+  gdbarch_get_syscalls_names_ftype *get_syscalls_names;
   int has_global_solist;
 };
 
@@ -372,6 +376,10 @@ struct gdbarch startup_gdbarch =
   default_target_signal_from_host,  /* target_signal_from_host */
   default_target_signal_to_host,  /* target_signal_to_host */
   0,  /* record_special_symbol */
+  0,  /* get_syscall_number */
+  0,  /* syscall_name_from_number */
+  0,  /* syscall_number_from_name */
+  0,  /* get_syscalls_names */
   0,  /* has_global_solist */
   /* startup_gdbarch() */
 };
@@ -625,6 +633,10 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of target_signal_from_host, invalid_p == 0 */
   /* Skip verify of target_signal_to_host, invalid_p == 0 */
   /* Skip verify of record_special_symbol, has predicate */
+  /* Skip verify of get_syscall_number, has predicate */
+  /* Skip verify of syscall_name_from_number, has predicate */
+  /* Skip verify of syscall_number_from_name, has predicate */
+  /* Skip verify of get_syscalls_names, has predicate */
   /* Skip verify of has_global_solist, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &dummy);
   make_cleanup (xfree, buf);
@@ -835,6 +847,18 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: get_longjmp_target = <0x%lx>\n",
                       (long) gdbarch->get_longjmp_target);
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_syscall_number_p() = %d\n",
+                      gdbarch_get_syscall_number_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_syscall_number = <0x%lx>\n",
+                      (long) gdbarch->get_syscall_number);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_syscalls_names_p() = %d\n",
+                      gdbarch_get_syscalls_names_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_syscalls_names = <0x%lx>\n",
+                      (long) gdbarch->get_syscalls_names);
+  fprintf_unfiltered (file,
                       "gdbarch_dump: has_global_solist = %s\n",
                       plongest (gdbarch->has_global_solist));
   fprintf_unfiltered (file,
@@ -1057,6 +1081,18 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: static_transform_name = <0x%lx>\n",
                       (long) gdbarch->static_transform_name);
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_syscall_name_from_number_p() = %d\n",
+                      gdbarch_syscall_name_from_number_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: syscall_name_from_number = <0x%lx>\n",
+                      (long) gdbarch->syscall_name_from_number);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_syscall_number_from_name_p() = %d\n",
+                      gdbarch_syscall_number_from_name_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: syscall_number_from_name = <0x%lx>\n",
+                      (long) gdbarch->syscall_number_from_name);
+  fprintf_unfiltered (file,
                       "gdbarch_dump: target_desc = %s\n",
                       plongest ((long) gdbarch->target_desc));
   fprintf_unfiltered (file,
@@ -3244,6 +3280,102 @@ 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_syscall_name_from_number_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->syscall_name_from_number != NULL;
+}
+
+const char *
+gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, int syscall_number)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->syscall_name_from_number != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_syscall_name_from_number called\n");
+  return gdbarch->syscall_name_from_number (gdbarch, syscall_number);
+}
+
+void
+set_gdbarch_syscall_name_from_number (struct gdbarch *gdbarch,
+                                      gdbarch_syscall_name_from_number_ftype syscall_name_from_number)
+{
+  gdbarch->syscall_name_from_number = syscall_name_from_number;
+}
+
+int
+gdbarch_syscall_number_from_name_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->syscall_number_from_name != NULL;
+}
+
+int
+gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, const char *syscall_name)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->syscall_number_from_name != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_syscall_number_from_name called\n");
+  return gdbarch->syscall_number_from_name (gdbarch, syscall_name);
+}
+
+void
+set_gdbarch_syscall_number_from_name (struct gdbarch *gdbarch,
+                                      gdbarch_syscall_number_from_name_ftype syscall_number_from_name)
+{
+  gdbarch->syscall_number_from_name = syscall_number_from_name;
+}
+
+int
+gdbarch_get_syscalls_names_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_syscalls_names != NULL;
+}
+
+const char **
+gdbarch_get_syscalls_names (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_syscalls_names != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_syscalls_names called\n");
+  return gdbarch->get_syscalls_names (gdbarch);
+}
+
+void
+set_gdbarch_get_syscalls_names (struct gdbarch *gdbarch,
+                                gdbarch_get_syscalls_names_ftype get_syscalls_names)
+{
+  gdbarch->get_syscalls_names = get_syscalls_names;
+}
+
+int
 gdbarch_has_global_solist (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 35f8a36..9cb6e84 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -811,6 +811,42 @@ 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);
 
+/* Functions for the 'catch syscall' feature.
+   Get architecture-specific system calls information from registers. */
+
+extern int gdbarch_get_syscall_number_p (struct gdbarch *gdbarch);
+
+typedef LONGEST (gdbarch_get_syscall_number_ftype) (struct gdbarch *gdbarch, ptid_t ptid);
+extern LONGEST gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid);
+extern void set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, gdbarch_get_syscall_number_ftype *get_syscall_number);
+
+/* Translate a syscall number to its corresponding name. */
+
+extern int gdbarch_syscall_name_from_number_p (struct gdbarch *gdbarch);
+
+typedef const char * (gdbarch_syscall_name_from_number_ftype) (struct gdbarch *gdbarch, int syscall_number);
+extern const char * gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, int syscall_number);
+extern void set_gdbarch_syscall_name_from_number (struct gdbarch *gdbarch, gdbarch_syscall_name_from_number_ftype *syscall_name_from_number);
+
+/* Translate a syscall name to its corresponding number.
+  
+   This function must return the syscall number if found, or
+   UNKNOWN_SYSCALL if not found. */
+
+extern int gdbarch_syscall_number_from_name_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_syscall_number_from_name_ftype) (struct gdbarch *gdbarch, const char *syscall_name);
+extern int gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, const char *syscall_name);
+extern void set_gdbarch_syscall_number_from_name (struct gdbarch *gdbarch, gdbarch_syscall_number_from_name_ftype *syscall_number_from_name);
+
+/* Returns the array containing the syscalls names for the architecture. */
+
+extern int gdbarch_get_syscalls_names_p (struct gdbarch *gdbarch);
+
+typedef const char ** (gdbarch_get_syscalls_names_ftype) (struct gdbarch *gdbarch);
+extern const char ** gdbarch_get_syscalls_names (struct gdbarch *gdbarch);
+extern void set_gdbarch_get_syscalls_names (struct gdbarch *gdbarch, gdbarch_get_syscalls_names_ftype *get_syscalls_names);
+
 /* True if the list of shared libraries is one and only for all
    processes, as opposed to a list of shared libraries per inferior.
    When this property is true, GDB assumes that since shared libraries
@@ -820,6 +856,9 @@ extern void set_gdbarch_record_special_symbol (struct gdbarch *gdbarch, gdbarch_
 extern int gdbarch_has_global_solist (struct gdbarch *gdbarch);
 extern void set_gdbarch_has_global_solist (struct gdbarch *gdbarch, int has_global_solist);
 
+/* 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 79ca862..2e65481 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -708,6 +708,23 @@ m:int:target_signal_to_host:enum target_signal ts:ts::default_target_signal_to_h
 # Record architecture-specific information from the symbol table.
 M:void:record_special_symbol:struct objfile *objfile, asymbol *sym:objfile, sym
 
+# Functions for the 'catch syscall' feature.
+
+# Get architecture-specific system calls information from registers.
+M:LONGEST:get_syscall_number:ptid_t ptid:ptid
+
+# Translate a syscall number to its corresponding name.
+M:const char *:syscall_name_from_number:int syscall_number:syscall_number
+
+# Translate a syscall name to its corresponding number.
+#
+# This function must return the syscall number if found, or
+# UNKNOWN_SYSCALL if not found.
+M:int:syscall_number_from_name:const char *syscall_name:syscall_name
+
+# Returns the array containing the syscalls names for the architecture.
+M:const char **:get_syscalls_names:void:
+
 # True if the list of shared libraries is one and only for all
 # processes, as opposed to a list of shared libraries per inferior.
 # When this property is true, GDB assumes that since shared libraries
@@ -895,6 +912,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/gdbthread.h b/gdb/gdbthread.h
index 55c848d..5d89ced 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -173,6 +173,13 @@ struct thread_info
 
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
+
+  /* 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;
 };
 
 /* Create an empty thread list, or empty the existing one.  */
diff --git a/gdb/inf-child.c b/gdb/inf-child.c
index 8da25f4..058cb34 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -144,6 +144,21 @@ inf_child_remove_exec_catchpoint (int pid)
   return 0;
 }
 
+static void
+inf_child_insert_syscall_catchpoint (int pid)
+{
+  /* This version of Unix doesn't support notification of syscall
+     events.  */
+}
+
+static int
+inf_child_remove_syscall_catchpoint (int pid)
+{
+  /* This version of Unix doesn't support notification of syscall
+     events.  */
+  return 0;
+}
+
 static int
 inf_child_can_run (void)
 {
@@ -187,6 +202,8 @@ 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_insert_syscall_catchpoint = inf_child_insert_syscall_catchpoint;
+  t->to_remove_syscall_catchpoint = inf_child_remove_syscall_catchpoint;
   t->to_can_run = inf_child_can_run;
   t->to_pid_to_exec_file = inf_child_pid_to_exec_file;
   t->to_stratum = process_stratum;
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 57af79a..65e7acd 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -355,13 +355,19 @@ static void
 inf_ptrace_resume (ptid_t ptid, int step, enum target_signal signal)
 {
   pid_t pid = ptid_get_pid (ptid);
-  int request = PT_CONTINUE;
+  int request;
 
   if (pid == -1)
     /* Resume all threads.  Traditionally ptrace() only supports
        single-threaded processes, so simply resume the inferior.  */
     pid = ptid_get_pid (inferior_ptid);
 
+  if (target_passed_by_entrypoint () > 0
+      && catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
   if (step)
     {
       /* If this system does not support PT_STEP, a higher level
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 50e8fff..744d32c 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -466,6 +466,11 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main)
   init_wait_for_inferior ();
   clear_breakpoint_hit_counts ();
 
+  /* If we already caught a syscall catchpoint, then reset its
+     syscall_number information because we are starting all over
+     again.  */
+  clear_syscall_catchpoints_info ();
+
   /* Clean up any leftovers from other runs.  Some other things from
      this function should probably be moved into target_pre_inferior.  */
   target_pre_inferior (from_tty);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 30d914d..843155c 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1042,7 +1042,7 @@ a command like `return' or `jump' to continue execution."));
         }
     }
 
-  /* If there were any forks/vforks/execs that were caught and are
+  /* If there were any forks/vforks/execs/syscalls that were caught and are
      now to be followed, then do so.  */
   switch (pending_follow.kind)
     {
@@ -1058,6 +1058,11 @@ a command like `return' or `jump' to continue execution."));
       pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
       break;
 
+    case TARGET_WAITKIND_SYSCALL_ENTRY:
+    case TARGET_WAITKIND_SYSCALL_RETURN:
+      pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+      break;
+
     default:
       break;
     }
@@ -1471,7 +1476,7 @@ init_wait_for_inferior (void)
 
   breakpoint_init_inferior (inf_starting);
 
-  /* The first resume is not following a fork/vfork/exec. */
+  /* The first resume is not following a fork/vfork/exec/syscall.  */
   pending_follow.kind = TARGET_WAITKIND_SPURIOUS;	/* I.e., none. */
 
   clear_proceed_status ();
@@ -2049,6 +2054,50 @@ ensure_not_running (void)
     error_is_running ();
 }
 
+/* Auxiliary function that handles syscall entry/return events.
+   It returns 1 if the inferior should keep going (and GDB
+   should ignore the event), or 0 if the event deserves to be
+   processed.  */
+static int
+deal_with_syscall_event (struct execution_control_state *ecs)
+{
+  int syscall_number = gdbarch_get_syscall_number (current_gdbarch,
+                                                   ecs->ptid);
+  if (catch_syscall_enabled () > 0
+      && catching_syscall_number (syscall_number) > 0)
+    {
+      ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
+      pending_follow.kind = ecs->ws.kind;
+
+      if (!ptid_equal (ecs->ptid, inferior_ptid))
+        {
+          context_switch (ecs->ptid);
+          reinit_frame_cache ();
+        }
+
+      stop_pc = read_pc ();
+
+      ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+
+      ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
+
+      /* If no catchpoint triggered for this, then keep going.  */
+      if (ecs->random_signal)
+        {
+          ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
+          keep_going (ecs);
+          return 1;
+        }
+      return 0;
+    }
+  else
+    {
+      resume (0, TARGET_SIGNAL_0);
+      prepare_to_wait (ecs);
+      return 1;
+    }
+}
+
 /* Given an execution control state that has been freshly filled in
    by an event from the inferior, figure out what it means and take
    appropriate action.  */
@@ -2337,9 +2386,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
@@ -2349,9 +2400,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)
@@ -3199,6 +3251,16 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
 	}
 	break;
 
+      case BPSTAT_WHAT_ENTRY_BREAKPOINT:
+        /* We hit the AT_ENTRY breakpoint, and now we have to enable
+           the PTRACE_O_TRACESYSGOOD option in the inferior *if* we
+           are catching syscalls.  */
+        if (debug_infrun)
+          fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_ENTRY_BREAKPOINT\n");
+        target_enable_tracesysgood (ecs->ptid);
+        ecs->event_thread->stepping_over_breakpoint = 1;
+        break;
+
       case BPSTAT_WHAT_LAST:
 	/* Not a real code, but listed here to shut up gcc -Wall.  */
 
@@ -4914,6 +4976,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/main.c b/gdb/main.c
index 15a6558..ebb19c6 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -62,6 +62,9 @@ int dbx_commands = 0;
 /* System root path, used to find libraries etc.  */
 char *gdb_sysroot = 0;
 
+/* GDB datadir, used to store data files.  */
+char *gdb_datadir = 0;
+
 struct ui_file *gdb_stdout;
 struct ui_file *gdb_stderr;
 struct ui_file *gdb_stdlog;
@@ -268,6 +271,40 @@ captured_main (void *data)
 	}
     }
 
+#ifdef GDB_DATADIR_RELOCATABLE
+  gdb_datadir = make_relative_prefix (argv[0], BINDIR, GDB_DATADIR);
+  if (gdb_datadir)
+    {
+      struct stat s;
+      int res = 0;
+
+      if (stat (gdb_datadir, &s) == 0)
+	if (S_ISDIR (s.st_mode))
+	  res = 1;
+
+      if (res == 0)
+	{
+	  xfree (gdb_datadir);
+	  gdb_datadir = xstrdup (GDB_DATADIR);
+	}
+    }
+  else
+    gdb_datadir = xstrdup (GDB_DATADIR);
+#else
+  gdb_datadir = xstrdup (GDB_DATADIR);
+#endif /* GDB_DATADIR_RELOCATABLE */
+
+  /* Canonicalize the GDB's datadir path.  */
+  if (*gdb_datadir)
+    {
+      char *canon_debug = lrealpath (gdb_datadir);
+      if (canon_debug)
+	{
+	  xfree (gdb_datadir);
+	  gdb_datadir = canon_debug;
+	}
+    }
+
   /* There will always be an interpreter.  Either the one passed into
      this captured main, or one specified by the user at start up, or
      the console.  Initialize the interpreter to the one requested by 
diff --git a/gdb/maint.c b/gdb/maint.c
index 365e374..3aae317 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -882,4 +882,12 @@ When enabled GDB is profiled."),
 			   show_maintenance_profile_p,
 			   &maintenance_set_cmdlist,
 			   &maintenance_show_cmdlist);
+  add_setshow_filename_cmd ("gdb_datadir", class_maintenance,
+                           &gdb_datadir, _("Set GDB's datadir path."),
+                           _("Show GDB's datadir path."),
+                           _("\
+When set, GDB uses the specified path to search for data files."),
+                           NULL, NULL,
+                           &maintenance_set_cmdlist,
+                           &maintenance_show_cmdlist);
 }
diff --git a/gdb/target.c b/gdb/target.c
index fa9a941..e66c430 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -438,6 +438,10 @@ update_current_target (void)
       /* Do not inherit to_follow_fork.  */
       INHERIT (to_insert_exec_catchpoint, t);
       INHERIT (to_remove_exec_catchpoint, t);
+      INHERIT (to_passed_by_entrypoint, t);
+      INHERIT (to_insert_syscall_catchpoint, t);
+      INHERIT (to_remove_syscall_catchpoint, t);
+      INHERIT (to_enable_tracesysgood, t);
       INHERIT (to_has_exited, t);
       INHERIT (to_mourn_inferior, t);
       INHERIT (to_can_run, t);
@@ -596,9 +600,21 @@ update_current_target (void)
   de_fault (to_insert_exec_catchpoint,
 	    (void (*) (int))
 	    tcomplain);
+  de_fault (to_passed_by_entrypoint,
+            (int (*) (void))
+            tcomplain);
   de_fault (to_remove_exec_catchpoint,
 	    (int (*) (int))
 	    tcomplain);
+  de_fault (to_insert_syscall_catchpoint,
+	    (void (*) (int))
+	    tcomplain);
+  de_fault (to_remove_syscall_catchpoint,
+	    (int (*) (int))
+	    tcomplain);
+  de_fault (to_enable_tracesysgood,
+            (void (*) (ptid_t))
+            tcomplain);
   de_fault (to_has_exited,
 	    (int (*) (int, int, int *))
 	    return_zero);
@@ -2562,6 +2578,12 @@ debug_to_wait (ptid_t ptid, struct target_waitstatus *status)
     case TARGET_WAITKIND_EXECD:
       fprintf_unfiltered (gdb_stdlog, "execd\n");
       break;
+    case TARGET_WAITKIND_SYSCALL_ENTRY:
+      fprintf_unfiltered (gdb_stdlog, "entered syscall\n");
+      break;
+    case TARGET_WAITKIND_SYSCALL_RETURN:
+      fprintf_unfiltered (gdb_stdlog, "exited syscall\n");
+      break;
     case TARGET_WAITKIND_SPURIOUS:
       fprintf_unfiltered (gdb_stdlog, "spurious\n");
       break;
diff --git a/gdb/target.h b/gdb/target.h
index a010d2d..1b935be 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -139,14 +139,15 @@ struct target_waitstatus
   {
     enum target_waitkind kind;
 
-    /* Forked child pid, execd pathname, exit status or signal number.  */
+    /* Forked child pid, execd pathname, exit status, signal number or
+       syscall name.  */
     union
       {
 	int integer;
 	enum target_signal sig;
 	ptid_t related_pid;
 	char *execd_pathname;
-	int syscall_id;
+	int syscall_number;
       }
     value;
   };
@@ -397,6 +398,10 @@ struct target_ops
     int (*to_follow_fork) (struct target_ops *, int);
     void (*to_insert_exec_catchpoint) (int);
     int (*to_remove_exec_catchpoint) (int);
+    int (*to_passed_by_entrypoint) (void);
+    void (*to_insert_syscall_catchpoint) (int);
+    int (*to_remove_syscall_catchpoint) (int);
+    void (*to_enable_tracesysgood) (ptid_t);
     int (*to_has_exited) (int, int, int *);
     void (*to_mourn_inferior) (void);
     int (*to_can_run) (void);
@@ -731,6 +736,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);
+
 /* From exec.c */
 
 extern void print_section_info (struct target_ops *, bfd *);
@@ -890,6 +897,24 @@ int target_follow_fork (int follow_child);
 #define target_remove_exec_catchpoint(pid) \
      (*current_target.to_remove_exec_catchpoint) (pid)
 
+/* Has the inferior already passed through its entrypoint? */
+#define target_passed_by_entrypoint() \
+     (*current_target.to_passed_by_entrypoint) ()
+
+/* Syscall catch functions */
+
+#define target_insert_syscall_catchpoint(pid) \
+     (*current_target.to_insert_syscall_catchpoint) (pid)
+
+#define target_remove_syscall_catchpoint(pid) \
+     (*current_target.to_remove_syscall_catchpoint) (pid)
+
+/* Enable PTRACE_O_TRACESYSGOOD in the inferior.
+   This is mainly used for the "catch syscall" feature.  */
+
+#define target_enable_tracesysgood(ptid) \
+     (*current_target.to_enable_tracesysgood) (ptid)
+
 /* Returns TRUE if PID has exited.  And, also sets EXIT_STATUS to the
    exit code of PID, if any.  */
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]