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. */ 25 enum 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. */ 36 enum catcher_action { 37 CATCH_ITER, 38 CATCH_ITER_1, 39 CATCH_THROWING 40 }; 41 42 struct 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(). */ 52 static std::forward_list<struct catcher> catchers; 53 54 jmp_buf * 55 exceptions_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 64 static int 65 exceptions_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 129 int 130 exceptions_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 154 int 155 exceptions_state_mc_action_iter (void) 156 { 157 return exceptions_state_mc (CATCH_ITER); 158 } 159 160 int 161 exceptions_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 168 void 169 throw_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 182 void 183 throw_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 193 static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) 194 throw_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 205 void 206 throw_verror (enum errors error, const char *fmt, va_list ap) 207 { 208 throw_it (RETURN_ERROR, error, fmt, ap); 209 } 210 211 void 212 throw_vquit (const char *fmt, va_list ap) 213 { 214 throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap); 215 } 216 217 void 218 throw_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 227 void 228 throw_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