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