Home | History | Annotate | Line # | Download | only in gdb
      1 /* Everything about syscall catchpoints, for GDB.
      2 
      3    Copyright (C) 2009-2024 Free Software Foundation, Inc.
      4 
      5    This file is part of GDB.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19 
     20 #include <ctype.h>
     21 #include "breakpoint.h"
     22 #include "cli/cli-cmds.h"
     23 #include "inferior.h"
     24 #include "cli/cli-utils.h"
     25 #include "annotate.h"
     26 #include "mi/mi-common.h"
     27 #include "valprint.h"
     28 #include "arch-utils.h"
     29 #include "observable.h"
     30 #include "xml-syscall.h"
     31 #include "cli/cli-style.h"
     32 #include "cli/cli-decode.h"
     33 
     34 /* An instance of this type is used to represent a syscall
     35    catchpoint.  */
     36 
     37 struct syscall_catchpoint : public catchpoint
     38 {
     39   syscall_catchpoint (struct gdbarch *gdbarch, bool tempflag,
     40 		      std::vector<int> &&calls)
     41     : catchpoint (gdbarch, tempflag, nullptr),
     42       syscalls_to_be_caught (std::move (calls))
     43   {
     44   }
     45 
     46   int insert_location (struct bp_location *) override;
     47   int remove_location (struct bp_location *,
     48 		       enum remove_bp_reason reason) override;
     49   int breakpoint_hit (const struct bp_location *bl,
     50 		      const address_space *aspace,
     51 		      CORE_ADDR bp_addr,
     52 		      const target_waitstatus &ws) override;
     53   enum print_stop_action print_it (const bpstat *bs) const override;
     54   bool print_one (const bp_location **) const override;
     55   void print_mention () const override;
     56   void print_recreate (struct ui_file *fp) const override;
     57 
     58   /* Syscall numbers used for the 'catch syscall' feature.  If no
     59      syscall has been specified for filtering, it is empty.
     60      Otherwise, it holds a list of all syscalls to be caught.  */
     61   std::vector<int> syscalls_to_be_caught;
     62 };
     63 
     64 struct catch_syscall_inferior_data
     65 {
     66   /* We keep a count of the number of times the user has requested a
     67      particular syscall to be tracked, and pass this information to the
     68      target.  This lets capable targets implement filtering directly.  */
     69 
     70   /* Number of times that "any" syscall is requested.  */
     71   int any_syscall_count;
     72 
     73   /* Count of each system call.  */
     74   std::vector<int> syscalls_counts;
     75 
     76   /* This counts all syscall catch requests, so we can readily determine
     77      if any catching is necessary.  */
     78   int total_syscalls_count;
     79 };
     80 
     81 static const registry<inferior>::key<catch_syscall_inferior_data>
     82   catch_syscall_inferior_data;
     83 
     84 static struct catch_syscall_inferior_data *
     85 get_catch_syscall_inferior_data (struct inferior *inf)
     86 {
     87   struct catch_syscall_inferior_data *inf_data;
     88 
     89   inf_data = catch_syscall_inferior_data.get (inf);
     90   if (inf_data == NULL)
     91     inf_data = catch_syscall_inferior_data.emplace (inf);
     92 
     93   return inf_data;
     94 }
     95 
     96 /* Implement the "insert" method for syscall catchpoints.  */
     97 
     98 int
     99 syscall_catchpoint::insert_location (struct bp_location *bl)
    100 {
    101   struct inferior *inf = current_inferior ();
    102   struct catch_syscall_inferior_data *inf_data
    103     = get_catch_syscall_inferior_data (inf);
    104 
    105   ++inf_data->total_syscalls_count;
    106   if (syscalls_to_be_caught.empty ())
    107     ++inf_data->any_syscall_count;
    108   else
    109     {
    110       for (int iter : syscalls_to_be_caught)
    111 	{
    112 	  if (iter >= inf_data->syscalls_counts.size ())
    113 	    inf_data->syscalls_counts.resize (iter + 1);
    114 	  ++inf_data->syscalls_counts[iter];
    115 	}
    116     }
    117 
    118   return target_set_syscall_catchpoint (inferior_ptid.pid (),
    119 					inf_data->total_syscalls_count != 0,
    120 					inf_data->any_syscall_count,
    121 					inf_data->syscalls_counts);
    122 }
    123 
    124 /* Implement the "remove" method for syscall catchpoints.  */
    125 
    126 int
    127 syscall_catchpoint::remove_location (struct bp_location *bl,
    128 				     enum remove_bp_reason reason)
    129 {
    130   struct inferior *inf = current_inferior ();
    131   struct catch_syscall_inferior_data *inf_data
    132     = get_catch_syscall_inferior_data (inf);
    133 
    134   --inf_data->total_syscalls_count;
    135   if (syscalls_to_be_caught.empty ())
    136     --inf_data->any_syscall_count;
    137   else
    138     {
    139       for (int iter : syscalls_to_be_caught)
    140 	{
    141 	  if (iter >= inf_data->syscalls_counts.size ())
    142 	    /* Shouldn't happen.  */
    143 	    continue;
    144 	  --inf_data->syscalls_counts[iter];
    145 	}
    146     }
    147 
    148   return target_set_syscall_catchpoint (inferior_ptid.pid (),
    149 					inf_data->total_syscalls_count != 0,
    150 					inf_data->any_syscall_count,
    151 					inf_data->syscalls_counts);
    152 }
    153 
    154 /* Implement the "breakpoint_hit" method for syscall catchpoints.  */
    155 
    156 int
    157 syscall_catchpoint::breakpoint_hit (const struct bp_location *bl,
    158 				    const address_space *aspace,
    159 				    CORE_ADDR bp_addr,
    160 				    const target_waitstatus &ws)
    161 {
    162   /* We must check if we are catching specific syscalls in this
    163      breakpoint.  If we are, then we must guarantee that the called
    164      syscall is the same syscall we are catching.  */
    165   int syscall_number = 0;
    166 
    167   if (ws.kind () != TARGET_WAITKIND_SYSCALL_ENTRY
    168       && ws.kind () != TARGET_WAITKIND_SYSCALL_RETURN)
    169     return 0;
    170 
    171   syscall_number = ws.syscall_number ();
    172 
    173   /* Now, checking if the syscall is the same.  */
    174   if (!syscalls_to_be_caught.empty ())
    175     {
    176       for (int iter : syscalls_to_be_caught)
    177 	if (syscall_number == iter)
    178 	  return 1;
    179 
    180       return 0;
    181     }
    182 
    183   return 1;
    184 }
    185 
    186 /* Implement the "print_it" method for syscall catchpoints.  */
    187 
    188 enum print_stop_action
    189 syscall_catchpoint::print_it (const bpstat *bs) const
    190 {
    191   struct ui_out *uiout = current_uiout;
    192   /* These are needed because we want to know in which state a
    193      syscall is.  It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
    194      or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we
    195      must print "called syscall" or "returned from syscall".  */
    196   struct target_waitstatus last;
    197   struct syscall s;
    198 
    199   get_last_target_status (nullptr, nullptr, &last);
    200 
    201   get_syscall_by_number (gdbarch, last.syscall_number (), &s);
    202 
    203   annotate_catchpoint (this->number);
    204   maybe_print_thread_hit_breakpoint (uiout);
    205 
    206   if (this->disposition == disp_del)
    207     uiout->text ("Temporary catchpoint ");
    208   else
    209     uiout->text ("Catchpoint ");
    210   if (uiout->is_mi_like_p ())
    211     {
    212       uiout->field_string ("reason",
    213 			   async_reason_lookup (last.kind () == TARGET_WAITKIND_SYSCALL_ENTRY
    214 						? EXEC_ASYNC_SYSCALL_ENTRY
    215 						: EXEC_ASYNC_SYSCALL_RETURN));
    216       uiout->field_string ("disp", bpdisp_text (this->disposition));
    217     }
    218   print_num_locno (bs, uiout);
    219 
    220   if (last.kind () == TARGET_WAITKIND_SYSCALL_ENTRY)
    221     uiout->text (" (call to syscall ");
    222   else
    223     uiout->text (" (returned from syscall ");
    224 
    225   if (s.name == NULL || uiout->is_mi_like_p ())
    226     uiout->field_signed ("syscall-number", last.syscall_number ());
    227   if (s.name != NULL)
    228     uiout->field_string ("syscall-name", s.name);
    229 
    230   uiout->text ("), ");
    231 
    232   return PRINT_SRC_AND_LOC;
    233 }
    234 
    235 /* Implement the "print_one" method for syscall catchpoints.  */
    236 
    237 bool
    238 syscall_catchpoint::print_one (const bp_location **last_loc) const
    239 {
    240   struct value_print_options opts;
    241   struct ui_out *uiout = current_uiout;
    242 
    243   get_user_print_options (&opts);
    244   /* Field 4, the address, is omitted (which makes the columns not
    245      line up too nicely with the headers, but the effect is relatively
    246      readable).  */
    247   if (opts.addressprint)
    248     uiout->field_skip ("addr");
    249   annotate_field (5);
    250 
    251   if (syscalls_to_be_caught.size () > 1)
    252     uiout->text ("syscalls \"");
    253   else
    254     uiout->text ("syscall \"");
    255 
    256   if (!syscalls_to_be_caught.empty ())
    257     {
    258       std::string text;
    259 
    260       bool first = true;
    261       for (int iter : syscalls_to_be_caught)
    262 	{
    263 	  struct syscall s;
    264 	  get_syscall_by_number (gdbarch, iter, &s);
    265 
    266 	  if (!first)
    267 	    text += ", ";
    268 	  first = false;
    269 
    270 	  if (s.name != NULL)
    271 	    text += s.name;
    272 	  else
    273 	    text += std::to_string (iter);
    274 	}
    275       uiout->field_string ("what", text.c_str ());
    276     }
    277   else
    278     uiout->field_string ("what", "<any syscall>", metadata_style.style ());
    279   uiout->text ("\" ");
    280 
    281   if (uiout->is_mi_like_p ())
    282     uiout->field_string ("catch-type", "syscall");
    283 
    284   return true;
    285 }
    286 
    287 /* Implement the "print_mention" method for syscall catchpoints.  */
    288 
    289 void
    290 syscall_catchpoint::print_mention () const
    291 {
    292   if (!syscalls_to_be_caught.empty ())
    293     {
    294       if (syscalls_to_be_caught.size () > 1)
    295 	gdb_printf (_("Catchpoint %d (syscalls"), number);
    296       else
    297 	gdb_printf (_("Catchpoint %d (syscall"), number);
    298 
    299       for (int iter : syscalls_to_be_caught)
    300 	{
    301 	  struct syscall s;
    302 	  get_syscall_by_number (gdbarch, iter, &s);
    303 
    304 	  if (s.name != NULL)
    305 	    gdb_printf (" '%s' [%d]", s.name, s.number);
    306 	  else
    307 	    gdb_printf (" %d", s.number);
    308 	}
    309       gdb_printf (")");
    310     }
    311   else
    312     gdb_printf (_("Catchpoint %d (any syscall)"), number);
    313 }
    314 
    315 /* Implement the "print_recreate" method for syscall catchpoints.  */
    316 
    317 void
    318 syscall_catchpoint::print_recreate (struct ui_file *fp) const
    319 {
    320   gdb_printf (fp, "catch syscall");
    321 
    322   for (int iter : syscalls_to_be_caught)
    323     {
    324       struct syscall s;
    325 
    326       get_syscall_by_number (gdbarch, iter, &s);
    327       if (s.name != NULL)
    328 	gdb_printf (fp, " %s", s.name);
    329       else
    330 	gdb_printf (fp, " %d", s.number);
    331     }
    332 
    333   print_recreate_thread (fp);
    334 }
    335 
    336 /* Returns non-zero if 'b' is a syscall catchpoint.  */
    337 
    338 static int
    339 syscall_catchpoint_p (struct breakpoint *b)
    340 {
    341   return dynamic_cast<syscall_catchpoint *> (b) != nullptr;
    342 }
    343 
    344 static void
    345 create_syscall_event_catchpoint (int tempflag, std::vector<int> &&filter)
    346 {
    347   struct gdbarch *gdbarch = get_current_arch ();
    348 
    349   std::unique_ptr<syscall_catchpoint> c
    350     (new syscall_catchpoint (gdbarch, tempflag, std::move (filter)));
    351 
    352   install_breakpoint (0, std::move (c), 1);
    353 }
    354 
    355 /* Splits the argument using space as delimiter.  */
    356 
    357 static std::vector<int>
    358 catch_syscall_split_args (const char *arg)
    359 {
    360   std::vector<int> result;
    361   gdbarch *gdbarch = current_inferior ()->arch ();
    362 
    363   while (*arg != '\0')
    364     {
    365       int i, syscall_number;
    366       char *endptr;
    367       char cur_name[128];
    368       struct syscall s;
    369 
    370       /* Skip whitespace.  */
    371       arg = skip_spaces (arg);
    372 
    373       for (i = 0; i < 127 && arg[i] && !isspace ((unsigned char)arg[i]); ++i)
    374 	cur_name[i] = arg[i];
    375       cur_name[i] = '\0';
    376       arg += i;
    377 
    378       /* Check if the user provided a syscall name, group, or a number.  */
    379       syscall_number = (int) strtol (cur_name, &endptr, 0);
    380       if (*endptr == '\0')
    381 	{
    382 	  if (syscall_number < 0)
    383 	    error (_("Unknown syscall number '%d'."), syscall_number);
    384 	  get_syscall_by_number (gdbarch, syscall_number, &s);
    385 	  result.push_back (s.number);
    386 	}
    387       else if (startswith (cur_name, "g:")
    388 	       || startswith (cur_name, "group:"))
    389 	{
    390 	  /* We have a syscall group.  Let's expand it into a syscall
    391 	     list before inserting.  */
    392 	  const char *group_name;
    393 
    394 	  /* Skip over "g:" and "group:" prefix strings.  */
    395 	  group_name = strchr (cur_name, ':') + 1;
    396 
    397 	  if (!get_syscalls_by_group (gdbarch, group_name, &result))
    398 	    error (_("Unknown syscall group '%s'."), group_name);
    399 	}
    400       else
    401 	{
    402 	  /* We have a name.  Let's check if it's valid and fetch a
    403 	     list of matching numbers.  */
    404 	  if (!get_syscalls_by_name (gdbarch, cur_name, &result))
    405 	    /* Here we have to issue an error instead of a warning,
    406 	       because GDB cannot do anything useful if there's no
    407 	       syscall number to be caught.  */
    408 	    error (_("Unknown syscall name '%s'."), cur_name);
    409 	}
    410     }
    411 
    412   return result;
    413 }
    414 
    415 /* Implement the "catch syscall" command.  */
    416 
    417 static void
    418 catch_syscall_command_1 (const char *arg, int from_tty,
    419 			 struct cmd_list_element *command)
    420 {
    421   int tempflag;
    422   std::vector<int> filter;
    423   struct syscall s;
    424   struct gdbarch *gdbarch = get_current_arch ();
    425 
    426   /* Checking if the feature if supported.  */
    427   if (gdbarch_get_syscall_number_p (gdbarch) == 0)
    428     error (_("The feature 'catch syscall' is not supported on \
    429 this architecture yet."));
    430 
    431   tempflag = command->context () == CATCH_TEMPORARY;
    432 
    433   arg = skip_spaces (arg);
    434 
    435   /* We need to do this first "dummy" translation in order
    436      to get the syscall XML file loaded or, most important,
    437      to display a warning to the user if there's no XML file
    438      for his/her architecture.  */
    439   get_syscall_by_number (gdbarch, 0, &s);
    440 
    441   /* The allowed syntax is:
    442      catch syscall
    443      catch syscall <name | number> [<name | number> ... <name | number>]
    444 
    445      Let's check if there's a syscall name.  */
    446 
    447   if (arg != NULL)
    448     filter = catch_syscall_split_args (arg);
    449 
    450   create_syscall_event_catchpoint (tempflag, std::move (filter));
    451 }
    452 
    453 
    454 /* Returns 0 if 'bp' is NOT a syscall catchpoint,
    455    non-zero otherwise.  */
    456 static int
    457 is_syscall_catchpoint_enabled (struct breakpoint *bp)
    458 {
    459   if (syscall_catchpoint_p (bp)
    460       && bp->enable_state != bp_disabled
    461       && bp->enable_state != bp_call_disabled)
    462     return 1;
    463   else
    464     return 0;
    465 }
    466 
    467 /* See breakpoint.h.  */
    468 
    469 bool
    470 catch_syscall_enabled ()
    471 {
    472   struct catch_syscall_inferior_data *inf_data
    473     = get_catch_syscall_inferior_data (current_inferior ());
    474 
    475   return inf_data->total_syscalls_count != 0;
    476 }
    477 
    478 /* Helper function for catching_syscall_number.  return true if B is a syscall
    479    catchpoint for SYSCALL_NUMBER, else false.  */
    480 
    481 static bool
    482 catching_syscall_number_1 (struct breakpoint *b, int syscall_number)
    483 {
    484   if (is_syscall_catchpoint_enabled (b))
    485     {
    486       syscall_catchpoint *c
    487 	= gdb::checked_static_cast<syscall_catchpoint *> (b);
    488 
    489       if (!c->syscalls_to_be_caught.empty ())
    490 	{
    491 	  for (int iter : c->syscalls_to_be_caught)
    492 	    if (syscall_number == iter)
    493 	      return true;
    494 	}
    495       else
    496 	return true;
    497     }
    498 
    499   return false;
    500 }
    501 
    502 bool
    503 catching_syscall_number (int syscall_number)
    504 {
    505   for (breakpoint &b : all_breakpoints ())
    506     if (catching_syscall_number_1 (&b, syscall_number))
    507       return true;
    508 
    509   return false;
    510 }
    511 
    512 /* Complete syscall names.  Used by "catch syscall".  */
    513 
    514 static void
    515 catch_syscall_completer (struct cmd_list_element *cmd,
    516 			 completion_tracker &tracker,
    517 			 const char *text, const char *word)
    518 {
    519   struct gdbarch *gdbarch = get_current_arch ();
    520   gdb::unique_xmalloc_ptr<const char *> group_list;
    521   const char *prefix;
    522 
    523   /* Completion considers ':' to be a word separator, so we use this to
    524      verify whether the previous word was a group prefix.  If so, we
    525      build the completion list using group names only.  */
    526   for (prefix = word; prefix != text && prefix[-1] != ' '; prefix--)
    527     ;
    528 
    529   if (startswith (prefix, "g:") || startswith (prefix, "group:"))
    530     {
    531       /* Perform completion inside 'group:' namespace only.  */
    532       group_list.reset (get_syscall_group_names (gdbarch));
    533       if (group_list != NULL)
    534 	complete_on_enum (tracker, group_list.get (), word, word);
    535     }
    536   else
    537     {
    538       /* Complete with both, syscall names and groups.  */
    539       gdb::unique_xmalloc_ptr<const char *> syscall_list
    540 	(get_syscall_names (gdbarch));
    541       group_list.reset (get_syscall_group_names (gdbarch));
    542 
    543       const char **group_ptr = group_list.get ();
    544 
    545       /* Hold on to strings while we're using them.  */
    546       std::vector<std::string> holders;
    547 
    548       /* Append "group:" prefix to syscall groups.  */
    549       for (int i = 0; group_ptr[i] != NULL; i++)
    550 	holders.push_back (string_printf ("group:%s", group_ptr[i]));
    551 
    552       for (int i = 0; group_ptr[i] != NULL; i++)
    553 	group_ptr[i] = holders[i].c_str ();
    554 
    555       if (syscall_list != NULL)
    556 	complete_on_enum (tracker, syscall_list.get (), word, word);
    557       if (group_list != NULL)
    558 	complete_on_enum (tracker, group_ptr, word, word);
    559     }
    560 }
    561 
    562 static void
    563 clear_syscall_counts (struct inferior *inf)
    564 {
    565   struct catch_syscall_inferior_data *inf_data
    566     = get_catch_syscall_inferior_data (inf);
    567 
    568   inf_data->total_syscalls_count = 0;
    569   inf_data->any_syscall_count = 0;
    570   inf_data->syscalls_counts.clear ();
    571 }
    572 
    573 void _initialize_break_catch_syscall ();
    574 void
    575 _initialize_break_catch_syscall ()
    576 {
    577   gdb::observers::inferior_exit.attach (clear_syscall_counts,
    578 					"break-catch-syscall");
    579 
    580   add_catch_command ("syscall", _("\
    581 Catch system calls by their names, groups and/or numbers.\n\
    582 Arguments say which system calls to catch.  If no arguments are given,\n\
    583 every system call will be caught.  Arguments, if given, should be one\n\
    584 or more system call names (if your system supports that), system call\n\
    585 groups or system call numbers."),
    586 		     catch_syscall_command_1,
    587 		     catch_syscall_completer,
    588 		     CATCH_PERMANENT,
    589 		     CATCH_TEMPORARY);
    590 }
    591