1/* Exception (throw catch) mechanism, for GDB, the GNU debugger. 2 3 Copyright (C) 1986-2023 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 "common-defs.h" 21#include "common-exceptions.h" 22#include <forward_list> 23 24/* Possible catcher states. */ 25enum catcher_state { 26 /* Initial state, a new catcher has just been created. */ 27 CATCHER_CREATED, 28 /* The catch code is running. */ 29 CATCHER_RUNNING, 30 CATCHER_RUNNING_1, 31 /* The catch code threw an exception. */ 32 CATCHER_ABORTING 33}; 34 35/* Possible catcher actions. */ 36enum catcher_action { 37 CATCH_ITER, 38 CATCH_ITER_1, 39 CATCH_THROWING 40}; 41 42struct catcher 43{ 44 enum catcher_state state = CATCHER_CREATED; 45 /* Jump buffer pointing back at the exception handler. */ 46 jmp_buf buf; 47 /* Status buffer belonging to the exception handler. */ 48 struct gdb_exception exception; 49}; 50 51/* Where to go for throw_exception(). */ 52static std::forward_list<struct catcher> catchers; 53 54jmp_buf * 55exceptions_state_mc_init () 56{ 57 catchers.emplace_front (); 58 return &catchers.front ().buf; 59} 60 61/* Catcher state machine. Returns non-zero if the m/c should be run 62 again, zero if it should abort. */ 63 64static int 65exceptions_state_mc (enum catcher_action action) 66{ 67 switch (catchers.front ().state) 68 { 69 case CATCHER_CREATED: 70 switch (action) 71 { 72 case CATCH_ITER: 73 /* Allow the code to run the catcher. */ 74 catchers.front ().state = CATCHER_RUNNING; 75 return 1; 76 default: 77 internal_error (_("bad state")); 78 } 79 case CATCHER_RUNNING: 80 switch (action) 81 { 82 case CATCH_ITER: 83 /* No error/quit has occured. */ 84 return 0; 85 case CATCH_ITER_1: 86 catchers.front ().state = CATCHER_RUNNING_1; 87 return 1; 88 case CATCH_THROWING: 89 catchers.front ().state = CATCHER_ABORTING; 90 /* See also throw_exception. */ 91 return 1; 92 default: 93 internal_error (_("bad switch")); 94 } 95 case CATCHER_RUNNING_1: 96 switch (action) 97 { 98 case CATCH_ITER: 99 /* The did a "break" from the inner while loop. */ 100 return 0; 101 case CATCH_ITER_1: 102 catchers.front ().state = CATCHER_RUNNING; 103 return 0; 104 case CATCH_THROWING: 105 catchers.front ().state = CATCHER_ABORTING; 106 /* See also throw_exception. */ 107 return 1; 108 default: 109 internal_error (_("bad switch")); 110 } 111 case CATCHER_ABORTING: 112 switch (action) 113 { 114 case CATCH_ITER: 115 { 116 /* Exit normally if this catcher can handle this 117 exception. The caller analyses the func return 118 values. */ 119 return 0; 120 } 121 default: 122 internal_error (_("bad state")); 123 } 124 default: 125 internal_error (_("bad switch")); 126 } 127} 128 129int 130exceptions_state_mc_catch (struct gdb_exception *exception, 131 int mask) 132{ 133 *exception = std::move (catchers.front ().exception); 134 catchers.pop_front (); 135 136 if (exception->reason < 0) 137 { 138 if (mask & RETURN_MASK (exception->reason)) 139 { 140 /* Exit normally and let the caller handle the 141 exception. */ 142 return 1; 143 } 144 145 /* The caller didn't request that the event be caught, relay the 146 event to the next exception_catch/CATCH_SJLJ. */ 147 throw_exception_sjlj (*exception); 148 } 149 150 /* No exception was thrown. */ 151 return 0; 152} 153 154int 155exceptions_state_mc_action_iter (void) 156{ 157 return exceptions_state_mc (CATCH_ITER); 158} 159 160int 161exceptions_state_mc_action_iter_1 (void) 162{ 163 return exceptions_state_mc (CATCH_ITER_1); 164} 165 166/* Return EXCEPTION to the nearest containing CATCH_SJLJ block. */ 167 168void 169throw_exception_sjlj (const struct gdb_exception &exception) 170{ 171 /* Jump to the nearest CATCH_SJLJ block, communicating REASON to 172 that call via setjmp's return value. Note that REASON can't be 173 zero, by definition in common-exceptions.h. */ 174 exceptions_state_mc (CATCH_THROWING); 175 enum return_reason reason = exception.reason; 176 catchers.front ().exception = exception; 177 longjmp (catchers.front ().buf, reason); 178} 179 180/* Implementation of throw_exception that uses C++ try/catch. */ 181 182void 183throw_exception (gdb_exception &&exception) 184{ 185 if (exception.reason == RETURN_QUIT) 186 throw gdb_exception_quit (std::move (exception)); 187 else if (exception.reason == RETURN_ERROR) 188 throw gdb_exception_error (std::move (exception)); 189 else 190 gdb_assert_not_reached ("invalid return reason"); 191} 192 193static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) 194throw_it (enum return_reason reason, enum errors error, const char *fmt, 195 va_list ap) 196{ 197 if (reason == RETURN_QUIT) 198 throw gdb_exception_quit (fmt, ap); 199 else if (reason == RETURN_ERROR) 200 throw gdb_exception_error (error, fmt, ap); 201 else 202 gdb_assert_not_reached ("invalid return reason"); 203} 204 205void 206throw_verror (enum errors error, const char *fmt, va_list ap) 207{ 208 throw_it (RETURN_ERROR, error, fmt, ap); 209} 210 211void 212throw_vquit (const char *fmt, va_list ap) 213{ 214 throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap); 215} 216 217void 218throw_error (enum errors error, const char *fmt, ...) 219{ 220 va_list args; 221 222 va_start (args, fmt); 223 throw_verror (error, fmt, args); 224 va_end (args); 225} 226 227void 228throw_quit (const char *fmt, ...) 229{ 230 va_list args; 231 232 va_start (args, fmt); 233 throw_vquit (fmt, args); 234 va_end (args); 235} 236