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