]>
Commit | Line | Data |
---|---|---|
2f9ee862 TT |
1 | /* Everything about load/unload catchpoints, for GDB. |
2 | ||
3 | Copyright (C) 1986-2022 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 | ||
22 | #include "annotate.h" | |
23 | #include "arch-utils.h" | |
24 | #include "breakpoint.h" | |
25 | #include "cli/cli-decode.h" | |
26 | #include "mi/mi-common.h" | |
27 | #include "progspace.h" | |
28 | #include "solist.h" | |
29 | #include "target.h" | |
30 | #include "valprint.h" | |
31 | ||
32 | /* An instance of this type is used to represent an solib catchpoint. | |
33 | A breakpoint is really of this type iff its ops pointer points to | |
34 | CATCH_SOLIB_BREAKPOINT_OPS. */ | |
35 | ||
36 | struct solib_catchpoint : public breakpoint | |
37 | { | |
38 | /* True for "catch load", false for "catch unload". */ | |
39 | bool is_load; | |
40 | ||
41 | /* Regular expression to match, if any. COMPILED is only valid when | |
42 | REGEX is non-NULL. */ | |
43 | gdb::unique_xmalloc_ptr<char> regex; | |
44 | std::unique_ptr<compiled_regex> compiled; | |
45 | }; | |
46 | ||
47 | static int | |
48 | insert_catch_solib (struct bp_location *ignore) | |
49 | { | |
50 | return 0; | |
51 | } | |
52 | ||
53 | static int | |
54 | remove_catch_solib (struct bp_location *ignore, enum remove_bp_reason reason) | |
55 | { | |
56 | return 0; | |
57 | } | |
58 | ||
59 | static int | |
60 | breakpoint_hit_catch_solib (const struct bp_location *bl, | |
61 | const address_space *aspace, | |
62 | CORE_ADDR bp_addr, | |
63 | const target_waitstatus &ws) | |
64 | { | |
65 | struct solib_catchpoint *self = (struct solib_catchpoint *) bl->owner; | |
66 | ||
67 | if (ws.kind () == TARGET_WAITKIND_LOADED) | |
68 | return 1; | |
69 | ||
70 | for (breakpoint *other : all_breakpoints ()) | |
71 | { | |
72 | if (other == bl->owner) | |
73 | continue; | |
74 | ||
75 | if (other->type != bp_shlib_event) | |
76 | continue; | |
77 | ||
78 | if (self->pspace != NULL && other->pspace != self->pspace) | |
79 | continue; | |
80 | ||
81 | for (bp_location *other_bl : other->locations ()) | |
82 | { | |
83 | if (other->ops->breakpoint_hit (other_bl, aspace, bp_addr, ws)) | |
84 | return 1; | |
85 | } | |
86 | } | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
91 | static void | |
92 | check_status_catch_solib (struct bpstat *bs) | |
93 | { | |
94 | struct solib_catchpoint *self | |
95 | = (struct solib_catchpoint *) bs->breakpoint_at; | |
96 | ||
97 | if (self->is_load) | |
98 | { | |
99 | for (so_list *iter : current_program_space->added_solibs) | |
100 | { | |
101 | if (!self->regex | |
102 | || self->compiled->exec (iter->so_name, 0, NULL, 0) == 0) | |
103 | return; | |
104 | } | |
105 | } | |
106 | else | |
107 | { | |
108 | for (const std::string &iter : current_program_space->deleted_solibs) | |
109 | { | |
110 | if (!self->regex | |
111 | || self->compiled->exec (iter.c_str (), 0, NULL, 0) == 0) | |
112 | return; | |
113 | } | |
114 | } | |
115 | ||
116 | bs->stop = 0; | |
117 | bs->print_it = print_it_noop; | |
118 | } | |
119 | ||
120 | static enum print_stop_action | |
121 | print_it_catch_solib (bpstat *bs) | |
122 | { | |
123 | struct breakpoint *b = bs->breakpoint_at; | |
124 | struct ui_out *uiout = current_uiout; | |
125 | ||
126 | annotate_catchpoint (b->number); | |
127 | maybe_print_thread_hit_breakpoint (uiout); | |
128 | if (b->disposition == disp_del) | |
129 | uiout->text ("Temporary catchpoint "); | |
130 | else | |
131 | uiout->text ("Catchpoint "); | |
132 | uiout->field_signed ("bkptno", b->number); | |
133 | uiout->text ("\n"); | |
134 | if (uiout->is_mi_like_p ()) | |
135 | uiout->field_string ("disp", bpdisp_text (b->disposition)); | |
136 | print_solib_event (1); | |
137 | return PRINT_SRC_AND_LOC; | |
138 | } | |
139 | ||
140 | static void | |
141 | print_one_catch_solib (struct breakpoint *b, struct bp_location **locs) | |
142 | { | |
143 | struct solib_catchpoint *self = (struct solib_catchpoint *) b; | |
144 | struct value_print_options opts; | |
145 | struct ui_out *uiout = current_uiout; | |
146 | ||
147 | get_user_print_options (&opts); | |
148 | /* Field 4, the address, is omitted (which makes the columns not | |
149 | line up too nicely with the headers, but the effect is relatively | |
150 | readable). */ | |
151 | if (opts.addressprint) | |
152 | { | |
153 | annotate_field (4); | |
154 | uiout->field_skip ("addr"); | |
155 | } | |
156 | ||
157 | std::string msg; | |
158 | annotate_field (5); | |
159 | if (self->is_load) | |
160 | { | |
161 | if (self->regex) | |
162 | msg = string_printf (_("load of library matching %s"), | |
163 | self->regex.get ()); | |
164 | else | |
165 | msg = _("load of library"); | |
166 | } | |
167 | else | |
168 | { | |
169 | if (self->regex) | |
170 | msg = string_printf (_("unload of library matching %s"), | |
171 | self->regex.get ()); | |
172 | else | |
173 | msg = _("unload of library"); | |
174 | } | |
175 | uiout->field_string ("what", msg); | |
176 | ||
177 | if (uiout->is_mi_like_p ()) | |
178 | uiout->field_string ("catch-type", self->is_load ? "load" : "unload"); | |
179 | } | |
180 | ||
181 | static void | |
182 | print_mention_catch_solib (struct breakpoint *b) | |
183 | { | |
184 | struct solib_catchpoint *self = (struct solib_catchpoint *) b; | |
185 | ||
186 | gdb_printf (_("Catchpoint %d (%s)"), b->number, | |
187 | self->is_load ? "load" : "unload"); | |
188 | } | |
189 | ||
190 | static void | |
191 | print_recreate_catch_solib (struct breakpoint *b, struct ui_file *fp) | |
192 | { | |
193 | struct solib_catchpoint *self = (struct solib_catchpoint *) b; | |
194 | ||
195 | gdb_printf (fp, "%s %s", | |
196 | b->disposition == disp_del ? "tcatch" : "catch", | |
197 | self->is_load ? "load" : "unload"); | |
198 | if (self->regex) | |
199 | gdb_printf (fp, " %s", self->regex.get ()); | |
200 | gdb_printf (fp, "\n"); | |
201 | } | |
202 | ||
203 | static struct breakpoint_ops catch_solib_breakpoint_ops; | |
204 | ||
205 | /* See breakpoint.h. */ | |
206 | ||
207 | void | |
208 | add_solib_catchpoint (const char *arg, bool is_load, bool is_temp, bool enabled) | |
209 | { | |
210 | struct gdbarch *gdbarch = get_current_arch (); | |
211 | ||
212 | if (!arg) | |
213 | arg = ""; | |
214 | arg = skip_spaces (arg); | |
215 | ||
216 | std::unique_ptr<solib_catchpoint> c (new solib_catchpoint ()); | |
217 | ||
218 | if (*arg != '\0') | |
219 | { | |
220 | c->compiled.reset (new compiled_regex (arg, REG_NOSUB, | |
221 | _("Invalid regexp"))); | |
222 | c->regex = make_unique_xstrdup (arg); | |
223 | } | |
224 | ||
225 | c->is_load = is_load; | |
226 | init_catchpoint (c.get (), gdbarch, is_temp, NULL, | |
227 | &catch_solib_breakpoint_ops); | |
228 | ||
229 | c->enable_state = enabled ? bp_enabled : bp_disabled; | |
230 | ||
231 | install_breakpoint (0, std::move (c), 1); | |
232 | } | |
233 | ||
234 | /* A helper function that does all the work for "catch load" and | |
235 | "catch unload". */ | |
236 | ||
237 | static void | |
238 | catch_load_or_unload (const char *arg, int from_tty, int is_load, | |
239 | struct cmd_list_element *command) | |
240 | { | |
241 | const int enabled = 1; | |
242 | bool temp = command->context () == CATCH_TEMPORARY; | |
243 | ||
244 | add_solib_catchpoint (arg, is_load, temp, enabled); | |
245 | } | |
246 | ||
247 | static void | |
248 | catch_load_command_1 (const char *arg, int from_tty, | |
249 | struct cmd_list_element *command) | |
250 | { | |
251 | catch_load_or_unload (arg, from_tty, 1, command); | |
252 | } | |
253 | ||
254 | static void | |
255 | catch_unload_command_1 (const char *arg, int from_tty, | |
256 | struct cmd_list_element *command) | |
257 | { | |
258 | catch_load_or_unload (arg, from_tty, 0, command); | |
259 | } | |
260 | ||
261 | static void | |
262 | initialize_ops () | |
263 | { | |
264 | struct breakpoint_ops *ops; | |
265 | ||
266 | initialize_breakpoint_ops (); | |
267 | ||
268 | /* Solib-related catchpoints. */ | |
269 | ops = &catch_solib_breakpoint_ops; | |
270 | *ops = base_breakpoint_ops; | |
271 | ops->insert_location = insert_catch_solib; | |
272 | ops->remove_location = remove_catch_solib; | |
273 | ops->breakpoint_hit = breakpoint_hit_catch_solib; | |
274 | ops->check_status = check_status_catch_solib; | |
275 | ops->print_it = print_it_catch_solib; | |
276 | ops->print_one = print_one_catch_solib; | |
277 | ops->print_mention = print_mention_catch_solib; | |
278 | ops->print_recreate = print_recreate_catch_solib; | |
279 | ||
280 | } | |
281 | ||
282 | void _initialize_break_catch_load (); | |
283 | void | |
284 | _initialize_break_catch_load () | |
285 | { | |
286 | initialize_ops (); | |
287 | ||
288 | add_catch_command ("load", _("Catch loads of shared libraries.\n\ | |
289 | Usage: catch load [REGEX]\n\ | |
290 | If REGEX is given, only stop for libraries matching the regular expression."), | |
291 | catch_load_command_1, | |
292 | NULL, | |
293 | CATCH_PERMANENT, | |
294 | CATCH_TEMPORARY); | |
295 | add_catch_command ("unload", _("Catch unloads of shared libraries.\n\ | |
296 | Usage: catch unload [REGEX]\n\ | |
297 | If REGEX is given, only stop for libraries matching the regular expression."), | |
298 | catch_unload_command_1, | |
299 | NULL, | |
300 | CATCH_PERMANENT, | |
301 | CATCH_TEMPORARY); | |
302 | } |