1 1.1 christos /* GDB/Scheme support for safe calls into the Guile interpreter. 2 1.1 christos 3 1.9 christos Copyright (C) 2014-2024 Free Software Foundation, Inc. 4 1.1 christos 5 1.1 christos This file is part of GDB. 6 1.1 christos 7 1.1 christos This program is free software; you can redistribute it and/or modify 8 1.1 christos it under the terms of the GNU General Public License as published by 9 1.1 christos the Free Software Foundation; either version 3 of the License, or 10 1.1 christos (at your option) any later version. 11 1.1 christos 12 1.1 christos This program is distributed in the hope that it will be useful, 13 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 14 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 1.1 christos GNU General Public License for more details. 16 1.1 christos 17 1.1 christos You should have received a copy of the GNU General Public License 18 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 1.1 christos 20 1.1 christos /* See README file in this directory for implementation notes, coding 21 1.1 christos conventions, et.al. */ 22 1.1 christos 23 1.1 christos #include "filenames.h" 24 1.1 christos #include "guile-internal.h" 25 1.7 christos #include "gdbsupport/pathstuff.h" 26 1.1 christos 27 1.1 christos /* Struct to marshall args to scscm_safe_call_body. */ 28 1.1 christos 29 1.1 christos struct c_data 30 1.1 christos { 31 1.4 christos const char *(*func) (void *); 32 1.1 christos void *data; 33 1.1 christos /* An error message or NULL for success. */ 34 1.4 christos const char *result; 35 1.1 christos }; 36 1.1 christos 37 1.1 christos /* Struct to marshall args through gdbscm_with_catch. */ 38 1.1 christos 39 1.1 christos struct with_catch_data 40 1.1 christos { 41 1.1 christos scm_t_catch_body func; 42 1.1 christos void *data; 43 1.1 christos scm_t_catch_handler unwind_handler; 44 1.1 christos scm_t_catch_handler pre_unwind_handler; 45 1.1 christos 46 1.1 christos /* If EXCP_MATCHER is non-NULL, it is an excp_matcher_func function. 47 1.1 christos If the exception is recognized by it, the exception is recorded as is, 48 1.1 christos without wrapping it in gdb:with-stack. */ 49 1.1 christos excp_matcher_func *excp_matcher; 50 1.1 christos 51 1.1 christos SCM stack; 52 1.1 christos SCM catch_result; 53 1.1 christos }; 54 1.1 christos 55 1.1 christos /* The "body" argument to scm_i_with_continuation_barrier. 56 1.1 christos Invoke the user-supplied function. */ 57 1.1 christos 58 1.1 christos static SCM 59 1.1 christos scscm_safe_call_body (void *d) 60 1.1 christos { 61 1.1 christos struct c_data *data = (struct c_data *) d; 62 1.1 christos 63 1.1 christos data->result = data->func (data->data); 64 1.1 christos 65 1.1 christos return SCM_UNSPECIFIED; 66 1.1 christos } 67 1.1 christos 68 1.1 christos /* A "pre-unwind handler" to scm_c_catch that prints the exception 69 1.1 christos according to "set guile print-stack". */ 70 1.1 christos 71 1.1 christos static SCM 72 1.1 christos scscm_printing_pre_unwind_handler (void *data, SCM key, SCM args) 73 1.1 christos { 74 1.1 christos SCM stack = scm_make_stack (SCM_BOOL_T, scm_list_1 (scm_from_int (2))); 75 1.1 christos 76 1.1 christos gdbscm_print_exception_with_stack (SCM_BOOL_F, stack, key, args); 77 1.1 christos 78 1.1 christos return SCM_UNSPECIFIED; 79 1.1 christos } 80 1.1 christos 81 1.1 christos /* A no-op unwind handler. */ 82 1.1 christos 83 1.1 christos static SCM 84 1.1 christos scscm_nop_unwind_handler (void *data, SCM key, SCM args) 85 1.1 christos { 86 1.1 christos return SCM_UNSPECIFIED; 87 1.1 christos } 88 1.1 christos 89 1.1 christos /* The "pre-unwind handler" to scm_c_catch that records the exception 90 1.1 christos for possible later printing. We do this in the pre-unwind handler because 91 1.1 christos we want the stack to include point where the exception occurred. 92 1.1 christos 93 1.1 christos If DATA is non-NULL, it is an excp_matcher_func function. 94 1.1 christos If the exception is recognized by it, the exception is recorded as is, 95 1.1 christos without wrapping it in gdb:with-stack. */ 96 1.1 christos 97 1.1 christos static SCM 98 1.1 christos scscm_recording_pre_unwind_handler (void *datap, SCM key, SCM args) 99 1.1 christos { 100 1.4 christos struct with_catch_data *data = (struct with_catch_data *) datap; 101 1.1 christos excp_matcher_func *matcher = data->excp_matcher; 102 1.1 christos 103 1.1 christos if (matcher != NULL && matcher (key)) 104 1.1 christos return SCM_UNSPECIFIED; 105 1.1 christos 106 1.1 christos /* There's no need to record the whole stack if we're not going to print it. 107 1.1 christos However, convention is to still print the stack frame in which the 108 1.1 christos exception occurred, even if we're not going to print a full backtrace. 109 1.1 christos For now, keep it simple. */ 110 1.1 christos 111 1.1 christos data->stack = scm_make_stack (SCM_BOOL_T, scm_list_1 (scm_from_int (2))); 112 1.1 christos 113 1.1 christos /* IWBN if we could return the <gdb:exception> here and skip the unwind 114 1.1 christos handler, but it doesn't work that way. If we want to return a 115 1.1 christos <gdb:exception> object from the catch it needs to come from the unwind 116 1.1 christos handler. So what we do is save the stack for later use by the unwind 117 1.1 christos handler. */ 118 1.1 christos 119 1.1 christos return SCM_UNSPECIFIED; 120 1.1 christos } 121 1.1 christos 122 1.1 christos /* Part two of the recording unwind handler. 123 1.1 christos Here we take the stack saved from the pre-unwind handler and create 124 1.1 christos the <gdb:exception> object. */ 125 1.1 christos 126 1.1 christos static SCM 127 1.1 christos scscm_recording_unwind_handler (void *datap, SCM key, SCM args) 128 1.1 christos { 129 1.4 christos struct with_catch_data *data = (struct with_catch_data *) datap; 130 1.1 christos 131 1.1 christos /* We need to record the stack in the exception since we're about to 132 1.1 christos throw and lose the location that got the exception. We do this by 133 1.1 christos wrapping the exception + stack in a new exception. */ 134 1.1 christos 135 1.1 christos if (gdbscm_is_true (data->stack)) 136 1.1 christos return gdbscm_make_exception_with_stack (key, args, data->stack); 137 1.1 christos 138 1.1 christos return gdbscm_make_exception (key, args); 139 1.1 christos } 140 1.1 christos 141 1.1 christos /* Ugh. :-( 142 1.1 christos Guile doesn't export scm_i_with_continuation_barrier which is exactly 143 1.1 christos what we need. To cope, have our own wrapper around scm_c_catch and 144 1.1 christos pass this as the "body" argument to scm_c_with_continuation_barrier. 145 1.1 christos Darn darn darn. */ 146 1.1 christos 147 1.1 christos static void * 148 1.1 christos gdbscm_with_catch (void *data) 149 1.1 christos { 150 1.4 christos struct with_catch_data *d = (struct with_catch_data *) data; 151 1.1 christos 152 1.1 christos d->catch_result 153 1.1 christos = scm_c_catch (SCM_BOOL_T, 154 1.1 christos d->func, d->data, 155 1.1 christos d->unwind_handler, d, 156 1.1 christos d->pre_unwind_handler, d); 157 1.1 christos 158 1.3 christos #if HAVE_GUILE_MANUAL_FINALIZATION 159 1.3 christos scm_run_finalizers (); 160 1.3 christos #endif 161 1.3 christos 162 1.1 christos return NULL; 163 1.1 christos } 164 1.1 christos 165 1.1 christos /* A wrapper around scm_with_guile that prints backtraces and exceptions 166 1.1 christos according to "set guile print-stack". 167 1.1 christos The result if NULL if no exception occurred, otherwise it is a statically 168 1.1 christos allocated error message (caller must *not* free). */ 169 1.1 christos 170 1.4 christos const char * 171 1.4 christos gdbscm_with_guile (const char *(*func) (void *), void *data) 172 1.1 christos { 173 1.1 christos struct c_data c_data; 174 1.1 christos struct with_catch_data catch_data; 175 1.1 christos 176 1.1 christos c_data.func = func; 177 1.1 christos c_data.data = data; 178 1.1 christos /* Set this now in case an exception is thrown. */ 179 1.1 christos c_data.result = _("Error while executing Scheme code."); 180 1.1 christos 181 1.1 christos catch_data.func = scscm_safe_call_body; 182 1.1 christos catch_data.data = &c_data; 183 1.1 christos catch_data.unwind_handler = scscm_nop_unwind_handler; 184 1.1 christos catch_data.pre_unwind_handler = scscm_printing_pre_unwind_handler; 185 1.1 christos catch_data.excp_matcher = NULL; 186 1.1 christos catch_data.stack = SCM_BOOL_F; 187 1.1 christos catch_data.catch_result = SCM_UNSPECIFIED; 188 1.1 christos 189 1.1 christos scm_with_guile (gdbscm_with_catch, &catch_data); 190 1.1 christos 191 1.1 christos return c_data.result; 192 1.1 christos } 193 1.1 christos 194 1.1 christos /* Another wrapper of scm_with_guile for use by the safe call/apply routines 195 1.1 christos in this file, as well as for general purpose calling other functions safely. 196 1.1 christos For these we want to record the exception, but leave the possible printing 197 1.1 christos of it to later. */ 198 1.1 christos 199 1.1 christos SCM 200 1.1 christos gdbscm_call_guile (SCM (*func) (void *), void *data, 201 1.1 christos excp_matcher_func *ok_excps) 202 1.1 christos { 203 1.1 christos struct with_catch_data catch_data; 204 1.1 christos 205 1.1 christos catch_data.func = func; 206 1.1 christos catch_data.data = data; 207 1.1 christos catch_data.unwind_handler = scscm_recording_unwind_handler; 208 1.1 christos catch_data.pre_unwind_handler = scscm_recording_pre_unwind_handler; 209 1.1 christos catch_data.excp_matcher = ok_excps; 210 1.1 christos catch_data.stack = SCM_BOOL_F; 211 1.1 christos catch_data.catch_result = SCM_UNSPECIFIED; 212 1.1 christos 213 1.1 christos #if 0 214 1.1 christos scm_c_with_continuation_barrier (gdbscm_with_catch, &catch_data); 215 1.1 christos #else 216 1.1 christos scm_with_guile (gdbscm_with_catch, &catch_data); 217 1.1 christos #endif 218 1.1 christos 219 1.1 christos return catch_data.catch_result; 220 1.1 christos } 221 1.1 christos 222 1.1 christos /* Utilities to safely call Scheme code, catching all exceptions, and 224 1.1 christos preventing continuation capture. 225 1.1 christos The result is the result of calling the function, or if an exception occurs 226 1.1 christos then the result is a <gdb:exception> smob, which can be tested for with 227 1.1 christos gdbscm_is_exception. */ 228 1.1 christos 229 1.1 christos /* Helper for gdbscm_safe_call_0. */ 230 1.1 christos 231 1.1 christos static SCM 232 1.1 christos scscm_call_0_body (void *argsp) 233 1.4 christos { 234 1.1 christos SCM *args = (SCM *) argsp; 235 1.1 christos 236 1.1 christos return scm_call_0 (args[0]); 237 1.1 christos } 238 1.1 christos 239 1.1 christos SCM 240 1.1 christos gdbscm_safe_call_0 (SCM proc, excp_matcher_func *ok_excps) 241 1.1 christos { 242 1.1 christos SCM args[] = { proc }; 243 1.1 christos 244 1.1 christos return gdbscm_call_guile (scscm_call_0_body, args, ok_excps); 245 1.1 christos } 246 1.1 christos 247 1.1 christos /* Helper for gdbscm_safe_call_1. */ 248 1.1 christos 249 1.1 christos static SCM 250 1.1 christos scscm_call_1_body (void *argsp) 251 1.4 christos { 252 1.1 christos SCM *args = (SCM *) argsp; 253 1.1 christos 254 1.1 christos return scm_call_1 (args[0], args[1]); 255 1.1 christos } 256 1.1 christos 257 1.1 christos SCM 258 1.1 christos gdbscm_safe_call_1 (SCM proc, SCM arg0, excp_matcher_func *ok_excps) 259 1.1 christos { 260 1.1 christos SCM args[] = { proc, arg0 }; 261 1.1 christos 262 1.1 christos return gdbscm_call_guile (scscm_call_1_body, args, ok_excps); 263 1.1 christos } 264 1.1 christos 265 1.1 christos /* Helper for gdbscm_safe_call_2. */ 266 1.1 christos 267 1.1 christos static SCM 268 1.1 christos scscm_call_2_body (void *argsp) 269 1.4 christos { 270 1.1 christos SCM *args = (SCM *) argsp; 271 1.1 christos 272 1.1 christos return scm_call_2 (args[0], args[1], args[2]); 273 1.1 christos } 274 1.1 christos 275 1.1 christos SCM 276 1.1 christos gdbscm_safe_call_2 (SCM proc, SCM arg0, SCM arg1, excp_matcher_func *ok_excps) 277 1.1 christos { 278 1.1 christos SCM args[] = { proc, arg0, arg1 }; 279 1.1 christos 280 1.1 christos return gdbscm_call_guile (scscm_call_2_body, args, ok_excps); 281 1.1 christos } 282 1.1 christos 283 1.1 christos /* Helper for gdbscm_safe_call_3. */ 284 1.1 christos 285 1.1 christos static SCM 286 1.1 christos scscm_call_3_body (void *argsp) 287 1.4 christos { 288 1.1 christos SCM *args = (SCM *) argsp; 289 1.1 christos 290 1.1 christos return scm_call_3 (args[0], args[1], args[2], args[3]); 291 1.1 christos } 292 1.1 christos 293 1.1 christos SCM 294 1.1 christos gdbscm_safe_call_3 (SCM proc, SCM arg1, SCM arg2, SCM arg3, 295 1.1 christos excp_matcher_func *ok_excps) 296 1.1 christos { 297 1.1 christos SCM args[] = { proc, arg1, arg2, arg3 }; 298 1.1 christos 299 1.1 christos return gdbscm_call_guile (scscm_call_3_body, args, ok_excps); 300 1.1 christos } 301 1.1 christos 302 1.1 christos /* Helper for gdbscm_safe_call_4. */ 303 1.1 christos 304 1.1 christos static SCM 305 1.1 christos scscm_call_4_body (void *argsp) 306 1.4 christos { 307 1.1 christos SCM *args = (SCM *) argsp; 308 1.1 christos 309 1.1 christos return scm_call_4 (args[0], args[1], args[2], args[3], args[4]); 310 1.1 christos } 311 1.1 christos 312 1.1 christos SCM 313 1.1 christos gdbscm_safe_call_4 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4, 314 1.1 christos excp_matcher_func *ok_excps) 315 1.1 christos { 316 1.1 christos SCM args[] = { proc, arg1, arg2, arg3, arg4 }; 317 1.1 christos 318 1.1 christos return gdbscm_call_guile (scscm_call_4_body, args, ok_excps); 319 1.1 christos } 320 1.1 christos 321 1.1 christos /* Helper for gdbscm_safe_apply_1. */ 322 1.1 christos 323 1.1 christos static SCM 324 1.1 christos scscm_apply_1_body (void *argsp) 325 1.4 christos { 326 1.1 christos SCM *args = (SCM *) argsp; 327 1.1 christos 328 1.1 christos return scm_apply_1 (args[0], args[1], args[2]); 329 1.1 christos } 330 1.1 christos 331 1.1 christos SCM 332 1.1 christos gdbscm_safe_apply_1 (SCM proc, SCM arg0, SCM rest, excp_matcher_func *ok_excps) 333 1.1 christos { 334 1.1 christos SCM args[] = { proc, arg0, rest }; 335 1.1 christos 336 1.1 christos return gdbscm_call_guile (scscm_apply_1_body, args, ok_excps); 337 1.1 christos } 338 1.1 christos 339 1.1 christos /* Utilities to call Scheme code, not catching exceptions, and 341 1.1 christos not preventing continuation capture. 342 1.1 christos The result is the result of calling the function. 343 1.1 christos If an exception occurs then Guile is left to handle the exception, 344 1.1 christos unwinding the stack as appropriate. 345 1.1 christos 346 1.1 christos USE THESE WITH CARE. 347 1.1 christos Typically these are called from functions that implement Scheme procedures, 348 1.1 christos and we don't want to catch the exception; otherwise it will get printed 349 1.1 christos twice: once when first caught and once if it ends up being rethrown and the 350 1.1 christos rethrow reaches the top repl, which will confuse the user. 351 1.1 christos 352 1.1 christos While these calls just pass the call off to the corresponding Guile 353 1.1 christos procedure, all such calls are routed through these ones to: 354 1.1 christos a) provide a place to put hooks or whatnot in if we need to, 355 1.1 christos b) add "unsafe" to the name to alert the reader. */ 356 1.1 christos 357 1.1 christos SCM 358 1.1 christos gdbscm_unsafe_call_1 (SCM proc, SCM arg0) 359 1.1 christos { 360 1.1 christos return scm_call_1 (proc, arg0); 361 1.1 christos } 362 1.1 christos 363 1.1 christos /* Utilities for safely evaluating a Scheme expression string. */ 365 1.1 christos 366 1.1 christos struct eval_scheme_string_data 367 1.1 christos { 368 1.1 christos const char *string; 369 1.1 christos int display_result; 370 1.1 christos }; 371 1.1 christos 372 1.4 christos /* Wrapper to eval a C string in the Guile interpreter. 373 1.1 christos This is passed to gdbscm_with_guile. */ 374 1.1 christos 375 1.4 christos static const char * 376 1.4 christos scscm_eval_scheme_string (void *datap) 377 1.1 christos { 378 1.1 christos struct eval_scheme_string_data *data 379 1.1 christos = (struct eval_scheme_string_data *) datap; 380 1.1 christos SCM result = scm_c_eval_string (data->string); 381 1.1 christos 382 1.1 christos if (data->display_result && !scm_is_eq (result, SCM_UNSPECIFIED)) 383 1.1 christos { 384 1.1 christos SCM port = scm_current_output_port (); 385 1.1 christos 386 1.1 christos scm_write (result, port); 387 1.1 christos scm_newline (port); 388 1.1 christos } 389 1.1 christos 390 1.1 christos /* If we get here the eval succeeded. */ 391 1.1 christos return NULL; 392 1.1 christos } 393 1.1 christos 394 1.1 christos /* Evaluate EXPR in the Guile interpreter, catching all exceptions 395 1.6 christos and preventing continuation capture. 396 1.1 christos The result is NULL if no exception occurred. Otherwise, the exception is 397 1.6 christos printed according to "set guile print-stack" and the result is an error 398 1.1 christos message. */ 399 1.1 christos 400 1.1 christos gdb::unique_xmalloc_ptr<char> 401 1.4 christos gdbscm_safe_eval_string (const char *string, int display_result) 402 1.1 christos { 403 1.1 christos struct eval_scheme_string_data data = { string, display_result }; 404 1.1 christos const char *result; 405 1.1 christos 406 1.7 christos result = gdbscm_with_guile (scscm_eval_scheme_string, (void *) &data); 407 1.1 christos 408 1.1 christos if (result != NULL) 409 1.1 christos return make_unique_xstrdup (result); 410 1.1 christos return NULL; 411 1.1 christos } 412 1.1 christos 413 1.1 christos /* Utilities for safely loading Scheme scripts. */ 415 1.1 christos 416 1.1 christos /* Helper function for gdbscm_safe_source_scheme_script. */ 417 1.4 christos 418 1.1 christos static const char * 419 1.1 christos scscm_source_scheme_script (void *data) 420 1.1 christos { 421 1.1 christos const char *filename = (const char *) data; 422 1.1 christos 423 1.1 christos /* The Guile docs don't specify what the result is. 424 1.1 christos Maybe it's SCM_UNSPECIFIED, but the docs should specify that. :-) */ 425 1.1 christos scm_c_primitive_load_path (filename); 426 1.1 christos 427 1.1 christos /* If we get here the load succeeded. */ 428 1.1 christos return NULL; 429 1.1 christos } 430 1.1 christos 431 1.1 christos /* Try to load a script, catching all exceptions, 432 1.1 christos and preventing continuation capture. 433 1.8 christos The result is NULL if the load succeeded. Otherwise, the exception is 434 1.1 christos printed according to "set guile print-stack" and the result is an error 435 1.1 christos message allocated with malloc, caller must free. */ 436 1.1 christos 437 1.1 christos gdb::unique_xmalloc_ptr<char> 438 1.1 christos gdbscm_safe_source_script (const char *filename) 439 1.1 christos { 440 1.1 christos /* scm_c_primitive_load_path only looks in %load-path for files with 441 1.6 christos relative paths. An alternative could be to temporarily add "." to 442 1.4 christos %load-path, but we don't want %load-path to be searched. At least not 443 1.1 christos by default. This function is invoked by the "source" GDB command which 444 1.1 christos already has its own path search support. */ 445 1.1 christos gdb::unique_xmalloc_ptr<char> abs_filename; 446 1.1 christos const char *result; 447 1.6 christos 448 1.1 christos if (!IS_ABSOLUTE_PATH (filename)) 449 1.1 christos { 450 1.1 christos abs_filename = gdb_realpath (filename); 451 1.1 christos filename = abs_filename.get (); 452 1.1 christos } 453 1.1 christos 454 1.8 christos result = gdbscm_with_guile (scscm_source_scheme_script, 455 1.1 christos (void *) filename); 456 1.1 christos 457 1.1 christos if (result != NULL) 458 1.1 christos return make_unique_xstrdup (result); 459 1.1 christos return NULL; 460 1.1 christos } 461 1.1 christos 462 1.1 christos /* Utility for entering an interactive Guile repl. */ 464 1.1 christos 465 1.1 christos void 466 1.1 christos gdbscm_enter_repl (void) 467 1.1 christos { 468 /* It's unfortunate to have to resort to something like this, but 469 scm_shell doesn't return. :-( I found this code on guile-user@. */ 470 gdbscm_safe_call_1 (scm_c_public_ref ("system repl repl", "start-repl"), 471 scm_from_latin1_symbol ("scheme"), NULL); 472 } 473