17d62b00eSchristos /* Exception (throw catch) mechanism, for GDB, the GNU debugger. 27d62b00eSchristos 3*6881a400Schristos Copyright (C) 1986-2023 Free Software Foundation, Inc. 47d62b00eSchristos 57d62b00eSchristos This file is part of GDB. 67d62b00eSchristos 77d62b00eSchristos This program is free software; you can redistribute it and/or modify 87d62b00eSchristos it under the terms of the GNU General Public License as published by 97d62b00eSchristos the Free Software Foundation; either version 3 of the License, or 107d62b00eSchristos (at your option) any later version. 117d62b00eSchristos 127d62b00eSchristos This program is distributed in the hope that it will be useful, 137d62b00eSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of 147d62b00eSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 157d62b00eSchristos GNU General Public License for more details. 167d62b00eSchristos 177d62b00eSchristos You should have received a copy of the GNU General Public License 187d62b00eSchristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 197d62b00eSchristos 207d62b00eSchristos #ifndef COMMON_COMMON_EXCEPTIONS_H 217d62b00eSchristos #define COMMON_COMMON_EXCEPTIONS_H 227d62b00eSchristos 237d62b00eSchristos #include <setjmp.h> 247d62b00eSchristos #include <new> 257d62b00eSchristos #include <memory> 267d62b00eSchristos #include <string> 27*6881a400Schristos #include <functional> 287d62b00eSchristos 297d62b00eSchristos /* Reasons for calling throw_exceptions(). NOTE: all reason values 307d62b00eSchristos must be different from zero. enum value 0 is reserved for internal 317d62b00eSchristos use as the return value from an initial setjmp(). */ 327d62b00eSchristos 337d62b00eSchristos enum return_reason 347d62b00eSchristos { 357d62b00eSchristos /* User interrupt. */ 367d62b00eSchristos RETURN_QUIT = -2, 377d62b00eSchristos /* Any other error. */ 387d62b00eSchristos RETURN_ERROR 397d62b00eSchristos }; 407d62b00eSchristos 417d62b00eSchristos #define RETURN_MASK(reason) (1 << (int)(-reason)) 427d62b00eSchristos 437d62b00eSchristos typedef enum 447d62b00eSchristos { 457d62b00eSchristos RETURN_MASK_QUIT = RETURN_MASK (RETURN_QUIT), 467d62b00eSchristos RETURN_MASK_ERROR = RETURN_MASK (RETURN_ERROR), 477d62b00eSchristos RETURN_MASK_ALL = (RETURN_MASK_QUIT | RETURN_MASK_ERROR) 487d62b00eSchristos } return_mask; 497d62b00eSchristos 507d62b00eSchristos /* Describe all exceptions. */ 517d62b00eSchristos 527d62b00eSchristos enum errors { 537d62b00eSchristos GDB_NO_ERROR, 547d62b00eSchristos 557d62b00eSchristos /* Any generic error, the corresponding text is in 567d62b00eSchristos exception.message. */ 577d62b00eSchristos GENERIC_ERROR, 587d62b00eSchristos 597d62b00eSchristos /* Something requested was not found. */ 607d62b00eSchristos NOT_FOUND_ERROR, 617d62b00eSchristos 627d62b00eSchristos /* Thread library lacks support necessary for finding thread local 637d62b00eSchristos storage. */ 647d62b00eSchristos TLS_NO_LIBRARY_SUPPORT_ERROR, 657d62b00eSchristos 667d62b00eSchristos /* Load module not found while attempting to find thread local storage. */ 677d62b00eSchristos TLS_LOAD_MODULE_NOT_FOUND_ERROR, 687d62b00eSchristos 697d62b00eSchristos /* Thread local storage has not been allocated yet. */ 707d62b00eSchristos TLS_NOT_ALLOCATED_YET_ERROR, 717d62b00eSchristos 727d62b00eSchristos /* Something else went wrong while attempting to find thread local 737d62b00eSchristos storage. The ``struct gdb_exception'' message field provides 747d62b00eSchristos more detail. */ 757d62b00eSchristos TLS_GENERIC_ERROR, 767d62b00eSchristos 777d62b00eSchristos /* Problem parsing an XML document. */ 787d62b00eSchristos XML_PARSE_ERROR, 797d62b00eSchristos 807d62b00eSchristos /* Error accessing memory. */ 817d62b00eSchristos MEMORY_ERROR, 827d62b00eSchristos 837d62b00eSchristos /* Value not available. E.g., a register was not collected in a 847d62b00eSchristos traceframe. */ 857d62b00eSchristos NOT_AVAILABLE_ERROR, 867d62b00eSchristos 877d62b00eSchristos /* Value was optimized out. Note: if the value was a register, this 887d62b00eSchristos means the register was not saved in the frame. */ 897d62b00eSchristos OPTIMIZED_OUT_ERROR, 907d62b00eSchristos 917d62b00eSchristos /* DW_OP_entry_value resolving failed. */ 927d62b00eSchristos NO_ENTRY_VALUE_ERROR, 937d62b00eSchristos 947d62b00eSchristos /* Target throwing an error has been closed. Current command should be 957d62b00eSchristos aborted as the inferior state is no longer valid. */ 967d62b00eSchristos TARGET_CLOSE_ERROR, 977d62b00eSchristos 987d62b00eSchristos /* An undefined command was executed. */ 997d62b00eSchristos UNDEFINED_COMMAND_ERROR, 1007d62b00eSchristos 1017d62b00eSchristos /* Requested feature, method, mechanism, etc. is not supported. */ 1027d62b00eSchristos NOT_SUPPORTED_ERROR, 1037d62b00eSchristos 1047d62b00eSchristos /* The number of candidates generated during line completion has 1057d62b00eSchristos reached the user's specified limit. This isn't an error, this exception 1067d62b00eSchristos is used to halt searching for more completions, but for consistency 1077d62b00eSchristos "_ERROR" is appended to the name. */ 1087d62b00eSchristos MAX_COMPLETIONS_REACHED_ERROR, 1097d62b00eSchristos 1107d62b00eSchristos /* Add more errors here. */ 1117d62b00eSchristos NR_ERRORS 1127d62b00eSchristos }; 1137d62b00eSchristos 1147d62b00eSchristos struct gdb_exception 1157d62b00eSchristos { 1167d62b00eSchristos gdb_exception () 1177d62b00eSchristos : reason ((enum return_reason) 0), 1187d62b00eSchristos error (GDB_NO_ERROR) 1197d62b00eSchristos { 1207d62b00eSchristos } 1217d62b00eSchristos 1227d62b00eSchristos gdb_exception (enum return_reason r, enum errors e) 1237d62b00eSchristos : reason (r), 1247d62b00eSchristos error (e) 1257d62b00eSchristos { 1267d62b00eSchristos } 1277d62b00eSchristos 1287d62b00eSchristos gdb_exception (enum return_reason r, enum errors e, 1297d62b00eSchristos const char *fmt, va_list ap) 1307d62b00eSchristos ATTRIBUTE_PRINTF (4, 0) 1317d62b00eSchristos : reason (r), 1327d62b00eSchristos error (e), 1337d62b00eSchristos message (std::make_shared<std::string> (string_vprintf (fmt, ap))) 1347d62b00eSchristos { 1357d62b00eSchristos } 1367d62b00eSchristos 1377d62b00eSchristos /* The move constructor exists so that we can mark it "noexcept", 1387d62b00eSchristos which is a good practice for any sort of exception object. */ 1397d62b00eSchristos explicit gdb_exception (gdb_exception &&other) noexcept = default; 1407d62b00eSchristos 1417d62b00eSchristos /* The copy constructor exists so that we can mark it "noexcept", 1427d62b00eSchristos which is a good practice for any sort of exception object. */ 1437d62b00eSchristos gdb_exception (const gdb_exception &other) noexcept 1447d62b00eSchristos : reason (other.reason), 1457d62b00eSchristos error (other.error), 1467d62b00eSchristos message (other.message) 1477d62b00eSchristos { 1487d62b00eSchristos } 1497d62b00eSchristos 1507d62b00eSchristos /* The assignment operator exists so that we can mark it "noexcept", 1517d62b00eSchristos which is a good practice for any sort of exception object. */ 1527d62b00eSchristos gdb_exception &operator= (const gdb_exception &other) noexcept 1537d62b00eSchristos { 1547d62b00eSchristos reason = other.reason; 1557d62b00eSchristos error = other.error; 1567d62b00eSchristos message = other.message; 1577d62b00eSchristos return *this; 1587d62b00eSchristos } 1597d62b00eSchristos 1607d62b00eSchristos gdb_exception &operator= (gdb_exception &&other) noexcept = default; 1617d62b00eSchristos 1627d62b00eSchristos /* Return the contents of the exception message, as a C string. The 1637d62b00eSchristos string remains owned by the exception object. */ 1647d62b00eSchristos const char *what () const noexcept 1657d62b00eSchristos { 1667d62b00eSchristos return message->c_str (); 1677d62b00eSchristos } 1687d62b00eSchristos 169*6881a400Schristos /* Compare two exceptions. */ 170*6881a400Schristos bool operator== (const gdb_exception &other) const 171*6881a400Schristos { 172*6881a400Schristos const char *msg1 = message == nullptr ? "" : what (); 173*6881a400Schristos const char *msg2 = other.message == nullptr ? "" : other.what (); 174*6881a400Schristos 175*6881a400Schristos return (reason == other.reason 176*6881a400Schristos && error == other.error 177*6881a400Schristos && strcmp (msg1, msg2) == 0); 178*6881a400Schristos } 179*6881a400Schristos 180*6881a400Schristos /* Compare two exceptions. */ 181*6881a400Schristos bool operator!= (const gdb_exception &other) const 182*6881a400Schristos { 183*6881a400Schristos return !(*this == other); 184*6881a400Schristos } 185*6881a400Schristos 1867d62b00eSchristos enum return_reason reason; 1877d62b00eSchristos enum errors error; 1887d62b00eSchristos std::shared_ptr<std::string> message; 1897d62b00eSchristos }; 1907d62b00eSchristos 191*6881a400Schristos namespace std 192*6881a400Schristos { 193*6881a400Schristos 194*6881a400Schristos /* Specialization of std::hash for gdb_exception. */ 195*6881a400Schristos template<> 196*6881a400Schristos struct hash<gdb_exception> 197*6881a400Schristos { 198*6881a400Schristos size_t operator() (const gdb_exception &exc) const 199*6881a400Schristos { 200*6881a400Schristos size_t result = exc.reason + exc.error; 201*6881a400Schristos if (exc.message != nullptr) 202*6881a400Schristos result += std::hash<std::string> {} (*exc.message); 203*6881a400Schristos return result; 204*6881a400Schristos } 205*6881a400Schristos }; 206*6881a400Schristos 207*6881a400Schristos } 208*6881a400Schristos 2097d62b00eSchristos /* Functions to drive the sjlj-based exceptions state machine. Though 2107d62b00eSchristos declared here by necessity, these functions should be considered 2117d62b00eSchristos internal to the exceptions subsystem and not used other than via 2127d62b00eSchristos the TRY/CATCH (or TRY_SJLJ/CATCH_SJLJ) macros defined below. */ 2137d62b00eSchristos 2147d62b00eSchristos extern jmp_buf *exceptions_state_mc_init (void); 2157d62b00eSchristos extern int exceptions_state_mc_action_iter (void); 2167d62b00eSchristos extern int exceptions_state_mc_action_iter_1 (void); 2177d62b00eSchristos extern int exceptions_state_mc_catch (struct gdb_exception *, int); 2187d62b00eSchristos 2197d62b00eSchristos /* Macro to wrap up standard try/catch behavior. 2207d62b00eSchristos 2217d62b00eSchristos The double loop lets us correctly handle code "break"ing out of the 2227d62b00eSchristos try catch block. (It works as the "break" only exits the inner 2237d62b00eSchristos "while" loop, the outer for loop detects this handling it 2247d62b00eSchristos correctly.) Of course "return" and "goto" are not so lucky. 2257d62b00eSchristos 2267d62b00eSchristos For instance: 2277d62b00eSchristos 2287d62b00eSchristos *INDENT-OFF* 2297d62b00eSchristos 2307d62b00eSchristos TRY_SJLJ 2317d62b00eSchristos { 2327d62b00eSchristos } 2337d62b00eSchristos CATCH_SJLJ (e, RETURN_MASK_ERROR) 2347d62b00eSchristos { 2357d62b00eSchristos switch (e.reason) 2367d62b00eSchristos { 2377d62b00eSchristos case RETURN_ERROR: ... 2387d62b00eSchristos } 2397d62b00eSchristos } 2407d62b00eSchristos END_CATCH_SJLJ 2417d62b00eSchristos 2427d62b00eSchristos The SJLJ variants are needed in some cases where gdb exceptions 2437d62b00eSchristos need to cross third-party library code compiled without exceptions 2447d62b00eSchristos support (e.g., readline). */ 2457d62b00eSchristos 2467d62b00eSchristos #define TRY_SJLJ \ 2477d62b00eSchristos { \ 2487d62b00eSchristos jmp_buf *buf = \ 2497d62b00eSchristos exceptions_state_mc_init (); \ 2507d62b00eSchristos setjmp (*buf); \ 2517d62b00eSchristos } \ 2527d62b00eSchristos while (exceptions_state_mc_action_iter ()) \ 2537d62b00eSchristos while (exceptions_state_mc_action_iter_1 ()) 2547d62b00eSchristos 2557d62b00eSchristos #define CATCH_SJLJ(EXCEPTION, MASK) \ 2567d62b00eSchristos { \ 2577d62b00eSchristos struct gdb_exception EXCEPTION; \ 2587d62b00eSchristos if (exceptions_state_mc_catch (&(EXCEPTION), MASK)) 2597d62b00eSchristos 2607d62b00eSchristos #define END_CATCH_SJLJ \ 2617d62b00eSchristos } 2627d62b00eSchristos 2637d62b00eSchristos /* The exception types client code may catch. They're just shims 2647d62b00eSchristos around gdb_exception that add nothing but type info. Which is used 2657d62b00eSchristos is selected depending on the MASK argument passed to CATCH. */ 2667d62b00eSchristos 2677d62b00eSchristos struct gdb_exception_error : public gdb_exception 2687d62b00eSchristos { 2697d62b00eSchristos gdb_exception_error (enum errors e, const char *fmt, va_list ap) 2707d62b00eSchristos ATTRIBUTE_PRINTF (3, 0) 2717d62b00eSchristos : gdb_exception (RETURN_ERROR, e, fmt, ap) 2727d62b00eSchristos { 2737d62b00eSchristos } 2747d62b00eSchristos 2757d62b00eSchristos explicit gdb_exception_error (gdb_exception &&ex) noexcept 2767d62b00eSchristos : gdb_exception (std::move (ex)) 2777d62b00eSchristos { 2787d62b00eSchristos gdb_assert (ex.reason == RETURN_ERROR); 2797d62b00eSchristos } 2807d62b00eSchristos }; 2817d62b00eSchristos 2827d62b00eSchristos struct gdb_exception_quit : public gdb_exception 2837d62b00eSchristos { 2847d62b00eSchristos gdb_exception_quit (const char *fmt, va_list ap) 2857d62b00eSchristos ATTRIBUTE_PRINTF (2, 0) 2867d62b00eSchristos : gdb_exception (RETURN_QUIT, GDB_NO_ERROR, fmt, ap) 2877d62b00eSchristos { 2887d62b00eSchristos } 2897d62b00eSchristos 2907d62b00eSchristos explicit gdb_exception_quit (gdb_exception &&ex) noexcept 2917d62b00eSchristos : gdb_exception (std::move (ex)) 2927d62b00eSchristos { 2937d62b00eSchristos gdb_assert (ex.reason == RETURN_QUIT); 2947d62b00eSchristos } 2957d62b00eSchristos }; 2967d62b00eSchristos 2977d62b00eSchristos /* An exception type that inherits from both std::bad_alloc and a gdb 2987d62b00eSchristos exception. This is necessary because operator new can only throw 2997d62b00eSchristos std::bad_alloc, and OTOH, we want exceptions thrown due to memory 3007d62b00eSchristos allocation error to be caught by all the CATCH/RETURN_MASK_ALL 3017d62b00eSchristos spread around the codebase. */ 3027d62b00eSchristos 3037d62b00eSchristos struct gdb_quit_bad_alloc 3047d62b00eSchristos : public gdb_exception_quit, 3057d62b00eSchristos public std::bad_alloc 3067d62b00eSchristos { 3077d62b00eSchristos explicit gdb_quit_bad_alloc (gdb_exception &&ex) noexcept 3087d62b00eSchristos : gdb_exception_quit (std::move (ex)), 3097d62b00eSchristos std::bad_alloc () 3107d62b00eSchristos { 3117d62b00eSchristos } 3127d62b00eSchristos }; 3137d62b00eSchristos 3147d62b00eSchristos /* *INDENT-ON* */ 3157d62b00eSchristos 3167d62b00eSchristos /* Throw an exception (as described by "struct gdb_exception"), 3177d62b00eSchristos landing in the inner most containing exception handler established 3187d62b00eSchristos using TRY/CATCH. */ 3197d62b00eSchristos extern void throw_exception (gdb_exception &&exception) 3207d62b00eSchristos ATTRIBUTE_NORETURN; 3217d62b00eSchristos 3227d62b00eSchristos /* Throw an exception by executing a LONG JUMP to the inner most 3237d62b00eSchristos containing exception handler established using TRY_SJLJ. Necessary 3247d62b00eSchristos in some cases where we need to throw GDB exceptions across 3257d62b00eSchristos third-party library code (e.g., readline). */ 3267d62b00eSchristos extern void throw_exception_sjlj (const struct gdb_exception &exception) 3277d62b00eSchristos ATTRIBUTE_NORETURN; 3287d62b00eSchristos 3297d62b00eSchristos /* Convenience wrappers around throw_exception that throw GDB 3307d62b00eSchristos errors. */ 3317d62b00eSchristos extern void throw_verror (enum errors, const char *fmt, va_list ap) 3327d62b00eSchristos ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0); 3337d62b00eSchristos extern void throw_vquit (const char *fmt, va_list ap) 3347d62b00eSchristos ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 0); 3357d62b00eSchristos extern void throw_error (enum errors error, const char *fmt, ...) 3367d62b00eSchristos ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3); 3377d62b00eSchristos extern void throw_quit (const char *fmt, ...) 3387d62b00eSchristos ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2); 3397d62b00eSchristos 3407d62b00eSchristos #endif /* COMMON_COMMON_EXCEPTIONS_H */ 341