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]

Re: [PATCH 1/4] catch syscall -- try 3 -- Architecture-independent part


Due to Eli's suggestions, I had to remake this part of the patch.

Regards,

On Tue, 2008-11-18 at 03:45 -0200, Sérgio Durigan Júnior wrote:
> Arch-independent part of the patch.
> 
> Regards,
> 
-- 
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 847dbdd..86735b2 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -192,6 +192,8 @@ static int is_hardware_watchpoint (struct breakpoint *bpt);
 
 static void insert_breakpoint_locations (void);
 
+static int syscall_catchpoint_p (struct breakpoint *b);
+
 static const char *
 bpdisp_text (enum bpdisp disp)
 {
@@ -342,6 +344,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
@@ -2250,6 +2264,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"));
@@ -3057,6 +3079,10 @@ bpstat_what (bpstat bs)
       /* We caught a shared library event.  */
       catch_shlib_event,
 
+      /* We are in a entry breakpoint.  It means we have stopped
+         at the inferior's entrypoint (AT_ENTRY).  */
+      entry_breakpoint,
+
       /* This is just used to count how many enums there are.  */
       class_last
     };
@@ -3073,6 +3099,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
@@ -3117,30 +3144,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
@@ -3154,6 +3184,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;
 
@@ -3247,6 +3278,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];
     }
@@ -3346,6 +3383,7 @@ print_one_breakpoint_location (struct breakpoint *b,
     {bp_thread_event, "thread events"},
     {bp_overlay_event, "overlay events"},
     {bp_catchpoint, "catchpoint"},
+    {bp_entry_breakpoint, "entry breakpoint"}
   };
   
   static char bpenables[] = "nynny";
@@ -3473,6 +3511,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);
@@ -4044,6 +4083,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:
       loc->loc_type = bp_loc_software_breakpoint;
@@ -4101,6 +4141,8 @@ set_raw_breakpoint_without_location (enum bptype bptype)
   b->frame_id = null_frame_id;
   b->forked_inferior_pid = null_ptid;
   b->exec_pathname = NULL;
+  b->syscalls_to_be_caught = NULL;
+  b->syscall_number = UNKNOWN_SYSCALL;
   b->ops = NULL;
   b->condition_not_parsed = 0;
 
@@ -4323,6 +4365,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)
 {
@@ -4620,7 +4687,238 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
   print_mention_catch_vfork
 };
 
-/* Create a new breakpoint of the bp_catchpoint kind and return it.
+/* 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.  */
+static int any_syscall_count;
+
+/* Count of each system call.  */
+static int *syscalls_counts;
+
+/* Number of system entries in SYSCALLS_COUNTS.  */
+static int syscalls_size;
+
+/* This counts all syscall catch requests, so we can readily determine
+   if any catching is necessary.  */
+static int total_syscalls_count;
+
+/* Implement the "insert" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static void
+insert_catch_syscall (struct breakpoint *b)
+{
+  ++total_syscalls_count;
+  if (!b->syscalls_to_be_caught)
+    ++any_syscall_count;
+  else
+    {
+      struct syscall_filter *iter;
+      for (iter = b->syscalls_to_be_caught; iter; iter = iter->next)
+	{
+	  if (iter->syscall >= syscalls_size)
+	    {
+	      syscalls_counts = xrealloc (syscalls_counts,
+					  (iter->syscall + 1) * sizeof (int));
+	      memset (&syscalls_counts[syscalls_size], 0,
+		      (iter->syscall + 1 - syscalls_size) * sizeof (int));
+	    }
+	  ++syscalls_counts[iter->syscall];
+	}
+    }
+
+  target_set_syscall_catchpoint (PIDGET (inferior_ptid),
+				 total_syscalls_count != 0,
+				 any_syscall_count,
+				 syscalls_size,
+				 syscalls_counts);
+}
+
+/* Implement the "remove" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static int
+remove_catch_syscall (struct breakpoint *b)
+{
+  --total_syscalls_count;
+  if (!b->syscalls_to_be_caught)
+    --any_syscall_count;
+  else
+    {
+      struct syscall_filter *iter;
+      for (iter = b->syscalls_to_be_caught; iter; iter = iter->next)
+	{
+	  if (iter->syscall >= syscalls_size)
+	    {
+	      /* Shouldn't happen.  */
+	      continue;
+	    }
+	  --syscalls_counts[iter->syscall];
+	}
+    }
+
+  return target_set_syscall_catchpoint (PIDGET (inferior_ptid),
+					total_syscalls_count != 0,
+					any_syscall_count,
+					syscalls_size,
+					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)
+    {
+      struct syscall_filter *iter;
+      for (iter = b->syscalls_to_be_caught; iter; iter = iter->next)
+	if (syscall_number == iter->syscall)
+	  break;
+      /* Not the same.  */
+      if (!iter)
+	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)
+{
+  /* 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;
+
+  gdbarch_get_syscall_by_number (current_gdbarch, b->syscall_number, &s);
+
+  get_last_target_status (&ptid, &last);
+
+  annotate_catchpoint (b->number);
+  printf_filtered (_("\nCatchpoint %d ("), b->number);
+
+  if (last.kind == TARGET_WAITKIND_SYSCALL_ENTRY)
+    printf_filtered (_("call to "));
+  else
+    printf_filtered (_("returned from "));
+
+  printf_filtered (_("syscall "));
+
+  if (s.name == NULL)
+    printf_filtered (_("%d"), b->syscall_number);
+  else
+    printf_filtered (_("'%s'"), s.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;
+  struct syscall s;
+
+  gdbarch_get_syscall_by_number (current_gdbarch, b->syscall_number, &s);
+
+  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 (s.name)
+        ui_out_field_string (uiout, "what", s.name);
+      else
+        ui_out_field_int (uiout, "what", b->syscall_number);
+    }
+  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)
+    {
+      struct syscall_filter *iter;
+      printf_filtered (_("Catchpoint %d (syscall(s)"), b->number);
+      for (iter = b->syscalls_to_be_caught; iter; iter = iter->next)
+        {
+          struct syscall s;
+          gdbarch_get_syscall_by_number (current_gdbarch, iter->syscall, &s);
+            
+          if (s.name)
+            printf_filtered (" '%s'", s.name);
+          else
+            printf_filtered (" %d", iter->syscall);
+        }
+      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.
@@ -4628,16 +4926,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);
@@ -4651,6 +4946,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);
 
@@ -4735,6 +5047,23 @@ static struct breakpoint_ops catch_exec_breakpoint_ops =
   print_mention_catch_exec
 };
 
+static void
+create_syscall_event_catchpoint (int tempflag, struct syscall_filter *filter,
+                                 struct breakpoint_ops *ops)
+{
+  struct breakpoint *b =
+    create_catchpoint_without_mention (tempflag, NULL, ops);
+
+  b->syscalls_to_be_caught = filter;
+  /* 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)
 {
@@ -4948,6 +5277,7 @@ mention (struct breakpoint *b)
       case bp_shlib_event:
       case bp_thread_event:
       case bp_overlay_event:
+      case bp_entry_breakpoint:
 	break;
       }
 
@@ -6610,6 +6940,122 @@ 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)
+{
+  struct syscall_filter *iter = *(struct syscall_filter **) arg;
+  while (iter)
+    {
+      struct syscall_filter *next = iter->next;
+      xfree (iter);
+      iter = next;
+    }
+}
+
+/* Splits the argument using space as delimiter.  Returns an xmalloc'd
+   filter list, or NULL if no filtering is required.  */
+static struct syscall_filter *
+catch_syscall_split_args (char *arg)
+{
+  struct syscall_filter *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_filter *new_filter;
+      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')
+        {
+          gdbarch_get_syscall_by_number (current_gdbarch,
+                                         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.  */
+          gdbarch_get_syscall_by_name (current_gdbarch, 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.  */
+      new_filter = XNEW (struct syscall_filter);
+      new_filter->syscall = s.number;
+      new_filter->next = result;
+      result = new_filter;
+    }
+
+  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;
+  struct syscall_filter *filter;
+  struct syscall s;
+
+  /* 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);
+
+  /* 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.  */
+  gdbarch_get_syscall_by_number (current_gdbarch, 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
@@ -7063,6 +7509,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?
@@ -7126,6 +7573,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;
@@ -7143,6 +7591,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);
 	  }
@@ -7449,6 +7898,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
@@ -8008,6 +8460,56 @@ 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)
+{
+  return 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)
+	  {
+	    struct syscall_filter *iter;
+	    for (iter = bp->syscalls_to_be_caught; iter; iter = iter->next)
+	      if (syscall_number == iter->syscall)
+		return 1;
+	  }
+	else
+	  return 1;
+      }
+
+  return 0;
+}
+
+/* Complete syscall names.  Used by "catch syscall".  */
+static char **
+catch_syscall_completer (char *text, char *word)
+{
+  const char **list =
+    gdbarch_get_syscall_names (current_gdbarch);
+  return (list == NULL) ? NULL : complete_on_enum (list, text, word);
+}
+
 
 /* This help string is used for the break, hbreak, tbreak and thbreak commands.
    It is defined as a macro to prevent duplication.
@@ -8040,6 +8542,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)
 {
@@ -8049,11 +8552,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
@@ -8328,36 +8833,50 @@ 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\
+With an argument, catch only that syscall."),
+                         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 cff6c3f..334e7ec 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,8 @@ struct block;
 
 #define	BREAKPOINT_MAX	16
 
-/* Type of breakpoint. */
+
+/* Type of breakpoint.  */
 /* FIXME In the future, we should fold all other breakpoint-like things into
    here.  This includes:
 
@@ -111,6 +112,10 @@ enum bptype
     bp_overlay_event, 
 
     bp_catchpoint,
+
+    /* This type is used to signal an internal breakpoint located at
+       the AT_ENTRY address.  */
+    bp_entry_breakpoint,
   };
 
 /* States of enablement of breakpoint. */
@@ -337,6 +342,17 @@ enum watchpoint_triggered
   watch_triggered_yes  
 };
 
+/* A syscall filter is represented as a linked list of syscall
+   numbers.  */
+struct syscall_filter
+{
+  /* The system call to accept.  */
+  int syscall;
+
+  /* The next filter.  */
+  struct syscall_filter *next;
+};
+
 typedef struct bp_location *bp_location_p;
 DEF_VEC_P(bp_location_p);
 
@@ -442,6 +458,20 @@ 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;
+
+    /* 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.  */
+    struct syscall_filter *syscalls_to_be_caught;
+
     /* Methods associated with this breakpoint.  */
     struct breakpoint_ops *ops;
 
@@ -523,6 +553,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
   };
@@ -786,6 +820,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 **);
@@ -860,6 +896,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/defs.h b/gdb/defs.h
index b047266..18b58ee 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..30e8fea 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -240,6 +240,11 @@ 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_get_syscall_by_number_ftype *get_syscall_by_number;
+  gdbarch_get_syscall_by_name_ftype *get_syscall_by_name;
+  gdbarch_get_syscall_names_ftype *get_syscall_names;
+  const char * xml_syscall_filename;
   int has_global_solist;
 };
 
@@ -372,6 +377,11 @@ 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,  /* get_syscall_by_number */
+  0,  /* get_syscall_by_name */
+  0,  /* get_syscall_names */
+  0,  /* xml_syscall_filename */
   0,  /* has_global_solist */
   /* startup_gdbarch() */
 };
@@ -625,6 +635,11 @@ 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 get_syscall_by_number, has predicate */
+  /* Skip verify of get_syscall_by_name, has predicate */
+  /* Skip verify of get_syscall_names, has predicate */
+  /* Skip verify of xml_syscall_filename, invalid_p == 0 */
   /* Skip verify of has_global_solist, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &dummy);
   make_cleanup (xfree, buf);
@@ -835,6 +850,30 @@ 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_by_name_p() = %d\n",
+                      gdbarch_get_syscall_by_name_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_syscall_by_name = <0x%lx>\n",
+                      (long) gdbarch->get_syscall_by_name);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_syscall_by_number_p() = %d\n",
+                      gdbarch_get_syscall_by_number_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_syscall_by_number = <0x%lx>\n",
+                      (long) gdbarch->get_syscall_by_number);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_syscall_names_p() = %d\n",
+                      gdbarch_get_syscall_names_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_syscall_names = <0x%lx>\n",
+                      (long) gdbarch->get_syscall_names);
+  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: has_global_solist = %s\n",
                       plongest (gdbarch->has_global_solist));
   fprintf_unfiltered (file,
@@ -1098,6 +1137,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file,
                       "gdbarch_dump: write_pc = <0x%lx>\n",
                       (long) gdbarch->write_pc);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: xml_syscall_filename = %s\n",
+                      gdbarch->xml_syscall_filename);
   if (gdbarch->dump_tdep != NULL)
     gdbarch->dump_tdep (gdbarch, file);
 }
@@ -3244,6 +3286,119 @@ 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_get_syscall_by_number_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_syscall_by_number != NULL;
+}
+
+void
+gdbarch_get_syscall_by_number (struct gdbarch *gdbarch, int syscall_number, struct syscall *s)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_syscall_by_number != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_syscall_by_number called\n");
+  gdbarch->get_syscall_by_number (gdbarch, syscall_number, s);
+}
+
+void
+set_gdbarch_get_syscall_by_number (struct gdbarch *gdbarch,
+                                   gdbarch_get_syscall_by_number_ftype get_syscall_by_number)
+{
+  gdbarch->get_syscall_by_number = get_syscall_by_number;
+}
+
+int
+gdbarch_get_syscall_by_name_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_syscall_by_name != NULL;
+}
+
+void
+gdbarch_get_syscall_by_name (struct gdbarch *gdbarch, const char *syscall_name, struct syscall *s)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_syscall_by_name != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_syscall_by_name called\n");
+  gdbarch->get_syscall_by_name (gdbarch, syscall_name, s);
+}
+
+void
+set_gdbarch_get_syscall_by_name (struct gdbarch *gdbarch,
+                                 gdbarch_get_syscall_by_name_ftype get_syscall_by_name)
+{
+  gdbarch->get_syscall_by_name = get_syscall_by_name;
+}
+
+int
+gdbarch_get_syscall_names_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_syscall_names != NULL;
+}
+
+const char **
+gdbarch_get_syscall_names (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_syscall_names != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_syscall_names called\n");
+  return gdbarch->get_syscall_names (gdbarch);
+}
+
+void
+set_gdbarch_get_syscall_names (struct gdbarch *gdbarch,
+                               gdbarch_get_syscall_names_ftype get_syscall_names)
+{
+  gdbarch->get_syscall_names = get_syscall_names;
+}
+
+const char *
+gdbarch_xml_syscall_filename (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of xml_syscall_filename, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_xml_syscall_filename called\n");
+  return gdbarch->xml_syscall_filename;
+}
+
+void
+set_gdbarch_xml_syscall_filename (struct gdbarch *gdbarch,
+                                  const char * xml_syscall_filename)
+{
+  gdbarch->xml_syscall_filename = xml_syscall_filename;
+}
+
+int
 gdbarch_has_global_solist (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 35f8a36..dd9950d 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;
 
 extern struct gdbarch *current_gdbarch;
 extern struct gdbarch *target_gdbarch;
@@ -811,6 +812,47 @@ 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);
+
+/* Fills the struct syscall (passed as argument) with the corresponding
+   system call represented by syscall_number. */
+
+extern int gdbarch_get_syscall_by_number_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_get_syscall_by_number_ftype) (struct gdbarch *gdbarch, int syscall_number, struct syscall *s);
+extern void gdbarch_get_syscall_by_number (struct gdbarch *gdbarch, int syscall_number, struct syscall *s);
+extern void set_gdbarch_get_syscall_by_number (struct gdbarch *gdbarch, gdbarch_get_syscall_by_number_ftype *get_syscall_by_number);
+
+/* Fills the struct syscall (passed as argument) with the corresponding
+   system call represented by syscall_name. */
+
+extern int gdbarch_get_syscall_by_name_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_get_syscall_by_name_ftype) (struct gdbarch *gdbarch, const char *syscall_name, struct syscall *s);
+extern void gdbarch_get_syscall_by_name (struct gdbarch *gdbarch, const char *syscall_name, struct syscall *s);
+extern void set_gdbarch_get_syscall_by_name (struct gdbarch *gdbarch, gdbarch_get_syscall_by_name_ftype *get_syscall_by_name);
+
+/* Returns the array containing the syscall names for the architecture. */
+
+extern int gdbarch_get_syscall_names_p (struct gdbarch *gdbarch);
+
+typedef const char ** (gdbarch_get_syscall_names_ftype) (struct gdbarch *gdbarch);
+extern const char ** gdbarch_get_syscall_names (struct gdbarch *gdbarch);
+extern void set_gdbarch_get_syscall_names (struct gdbarch *gdbarch, gdbarch_get_syscall_names_ftype *get_syscall_names);
+
+/* Stores the name of syscall's XML file. Contains NULL if the file
+   was not set. */
+
+extern const char * gdbarch_xml_syscall_filename (struct gdbarch *gdbarch);
+extern void set_gdbarch_xml_syscall_filename (struct gdbarch *gdbarch, const char * xml_syscall_filename);
+
 /* 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 +862,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..16493be 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -708,6 +708,26 @@ 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
+
+# Fills the struct syscall (passed as argument) with the corresponding
+# system call represented by syscall_number.
+M:void:get_syscall_by_number:int syscall_number, struct syscall *s:syscall_number, s
+
+# Fills the struct syscall (passed as argument) with the corresponding
+# system call represented by syscall_name.
+M:void:get_syscall_by_name:const char *syscall_name, struct syscall *s:syscall_name, s
+
+# Returns the array containing the syscall names for the architecture.
+M:const char **:get_syscall_names:void:
+
+# Stores the name of syscall's XML file. Contains NULL if the file
+# was not set.
+v:const char *:xml_syscall_filename:::0:0::0:gdbarch->xml_syscall_filename
+
 # 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
@@ -826,6 +846,7 @@ struct bp_target_info;
 struct target_desc;
 struct displaced_step_closure;
 struct core_regset_section;
+struct syscall;
 
 extern struct gdbarch *current_gdbarch;
 extern struct gdbarch *target_gdbarch;
@@ -895,6 +916,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/inf-child.c b/gdb/inf-child.c
index 8da25f4..8991a8b 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -145,6 +145,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;
@@ -187,6 +196,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 bf3d0a8..d854641 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -350,13 +350,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 810b3b7..653c9a3 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -464,6 +464,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 fbe0af0..9ce559a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1044,7 +1044,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)
     {
@@ -1060,6 +1060,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;
     }
@@ -1501,7 +1506,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 ();
@@ -2079,6 +2084,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.  */
@@ -2367,9 +2416,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
@@ -2379,9 +2430,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)
@@ -3201,6 +3253,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.  */
 
@@ -4966,6 +5028,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 c46bdc0..c184fec 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -906,4 +906,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 3901ee7..0e16512 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -456,6 +456,9 @@ 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_set_syscall_catchpoint, t);
+      INHERIT (to_enable_tracesysgood, t);
       INHERIT (to_has_exited, t);
       /* Do no inherit to_mourn_inferiour.  */
       INHERIT (to_can_run, t);
@@ -611,9 +614,18 @@ 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_set_syscall_catchpoint,
+	    (int (*) (int, int, int, int, int *))
+	    tcomplain);
+  de_fault (to_enable_tracesysgood,
+            (void (*) (ptid_t))
+            tcomplain);
   de_fault (to_has_exited,
 	    (int (*) (int, int, int *))
 	    return_zero);
@@ -2623,6 +2635,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 05b681d..6b4c562 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -139,18 +139,34 @@ 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;
   };
 
+/* 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;
+  };
+
 /* Possible types of events that the inferior handler will have to
    deal with.  */
 enum inferior_event_type
@@ -398,6 +414,9 @@ 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);
+    int (*to_set_syscall_catchpoint) (int, int, int, int, int *);
+    void (*to_enable_tracesysgood) (ptid_t);
     int (*to_has_exited) (int, int, int *);
     void (*to_mourn_inferior) (struct target_ops *);
     int (*to_can_run) (void);
@@ -731,6 +750,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 *);
@@ -889,6 +910,27 @@ 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.  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)
+
+/* 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]