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