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