18dffb485Schristos /* Exception (throw catch) mechanism, for GDB, the GNU debugger. 28dffb485Schristos 3*5ba1f45fSchristos Copyright (C) 1986-2024 Free Software Foundation, Inc. 48dffb485Schristos 58dffb485Schristos This file is part of GDB. 68dffb485Schristos 78dffb485Schristos This program is free software; you can redistribute it and/or modify 88dffb485Schristos it under the terms of the GNU General Public License as published by 98dffb485Schristos the Free Software Foundation; either version 3 of the License, or 108dffb485Schristos (at your option) any later version. 118dffb485Schristos 128dffb485Schristos This program is distributed in the hope that it will be useful, 138dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 148dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158dffb485Schristos GNU General Public License for more details. 168dffb485Schristos 178dffb485Schristos You should have received a copy of the GNU General Public License 188dffb485Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 198dffb485Schristos 208dffb485Schristos #include "common-exceptions.h" 218dffb485Schristos #include <forward_list> 228dffb485Schristos 238dffb485Schristos /* Possible catcher states. */ 248dffb485Schristos enum catcher_state { 258dffb485Schristos /* Initial state, a new catcher has just been created. */ 268dffb485Schristos CATCHER_CREATED, 278dffb485Schristos /* The catch code is running. */ 288dffb485Schristos CATCHER_RUNNING, 298dffb485Schristos CATCHER_RUNNING_1, 308dffb485Schristos /* The catch code threw an exception. */ 318dffb485Schristos CATCHER_ABORTING 328dffb485Schristos }; 338dffb485Schristos 348dffb485Schristos /* Possible catcher actions. */ 358dffb485Schristos enum catcher_action { 368dffb485Schristos CATCH_ITER, 378dffb485Schristos CATCH_ITER_1, 388dffb485Schristos CATCH_THROWING 398dffb485Schristos }; 408dffb485Schristos 418dffb485Schristos struct catcher 428dffb485Schristos { 438dffb485Schristos enum catcher_state state = CATCHER_CREATED; 448dffb485Schristos /* Jump buffer pointing back at the exception handler. */ 458dffb485Schristos jmp_buf buf; 468dffb485Schristos /* Status buffer belonging to the exception handler. */ 478dffb485Schristos struct gdb_exception exception; 488dffb485Schristos }; 498dffb485Schristos 508dffb485Schristos /* Where to go for throw_exception(). */ 518dffb485Schristos static std::forward_list<struct catcher> catchers; 528dffb485Schristos 538dffb485Schristos jmp_buf * 548dffb485Schristos exceptions_state_mc_init () 558dffb485Schristos { 568dffb485Schristos catchers.emplace_front (); 578dffb485Schristos return &catchers.front ().buf; 588dffb485Schristos } 598dffb485Schristos 608dffb485Schristos /* Catcher state machine. Returns non-zero if the m/c should be run 618dffb485Schristos again, zero if it should abort. */ 628dffb485Schristos 638dffb485Schristos static int 648dffb485Schristos exceptions_state_mc (enum catcher_action action) 658dffb485Schristos { 668dffb485Schristos switch (catchers.front ().state) 678dffb485Schristos { 688dffb485Schristos case CATCHER_CREATED: 698dffb485Schristos switch (action) 708dffb485Schristos { 718dffb485Schristos case CATCH_ITER: 728dffb485Schristos /* Allow the code to run the catcher. */ 738dffb485Schristos catchers.front ().state = CATCHER_RUNNING; 748dffb485Schristos return 1; 758dffb485Schristos default: 764b169a6bSchristos internal_error (_("bad state")); 778dffb485Schristos } 788dffb485Schristos case CATCHER_RUNNING: 798dffb485Schristos switch (action) 808dffb485Schristos { 818dffb485Schristos case CATCH_ITER: 82*5ba1f45fSchristos /* No error/quit has occurred. */ 838dffb485Schristos return 0; 848dffb485Schristos case CATCH_ITER_1: 858dffb485Schristos catchers.front ().state = CATCHER_RUNNING_1; 868dffb485Schristos return 1; 878dffb485Schristos case CATCH_THROWING: 888dffb485Schristos catchers.front ().state = CATCHER_ABORTING; 898dffb485Schristos /* See also throw_exception. */ 908dffb485Schristos return 1; 918dffb485Schristos default: 924b169a6bSchristos internal_error (_("bad switch")); 938dffb485Schristos } 948dffb485Schristos case CATCHER_RUNNING_1: 958dffb485Schristos switch (action) 968dffb485Schristos { 978dffb485Schristos case CATCH_ITER: 988dffb485Schristos /* The did a "break" from the inner while loop. */ 998dffb485Schristos return 0; 1008dffb485Schristos case CATCH_ITER_1: 1018dffb485Schristos catchers.front ().state = CATCHER_RUNNING; 1028dffb485Schristos return 0; 1038dffb485Schristos case CATCH_THROWING: 1048dffb485Schristos catchers.front ().state = CATCHER_ABORTING; 1058dffb485Schristos /* See also throw_exception. */ 1068dffb485Schristos return 1; 1078dffb485Schristos default: 1084b169a6bSchristos internal_error (_("bad switch")); 1098dffb485Schristos } 1108dffb485Schristos case CATCHER_ABORTING: 1118dffb485Schristos switch (action) 1128dffb485Schristos { 1138dffb485Schristos case CATCH_ITER: 1148dffb485Schristos { 1158dffb485Schristos /* Exit normally if this catcher can handle this 1168dffb485Schristos exception. The caller analyses the func return 1178dffb485Schristos values. */ 1188dffb485Schristos return 0; 1198dffb485Schristos } 1208dffb485Schristos default: 1214b169a6bSchristos internal_error (_("bad state")); 1228dffb485Schristos } 1238dffb485Schristos default: 1244b169a6bSchristos internal_error (_("bad switch")); 1258dffb485Schristos } 1268dffb485Schristos } 1278dffb485Schristos 1288dffb485Schristos int 1298dffb485Schristos exceptions_state_mc_catch (struct gdb_exception *exception, 1308dffb485Schristos int mask) 1318dffb485Schristos { 1328dffb485Schristos *exception = std::move (catchers.front ().exception); 1338dffb485Schristos catchers.pop_front (); 1348dffb485Schristos 1358dffb485Schristos if (exception->reason < 0) 1368dffb485Schristos { 1378dffb485Schristos if (mask & RETURN_MASK (exception->reason)) 1388dffb485Schristos { 1398dffb485Schristos /* Exit normally and let the caller handle the 1408dffb485Schristos exception. */ 1418dffb485Schristos return 1; 1428dffb485Schristos } 1438dffb485Schristos 1448dffb485Schristos /* The caller didn't request that the event be caught, relay the 1458dffb485Schristos event to the next exception_catch/CATCH_SJLJ. */ 1468dffb485Schristos throw_exception_sjlj (*exception); 1478dffb485Schristos } 1488dffb485Schristos 1498dffb485Schristos /* No exception was thrown. */ 1508dffb485Schristos return 0; 1518dffb485Schristos } 1528dffb485Schristos 1538dffb485Schristos int 1548dffb485Schristos exceptions_state_mc_action_iter (void) 1558dffb485Schristos { 1568dffb485Schristos return exceptions_state_mc (CATCH_ITER); 1578dffb485Schristos } 1588dffb485Schristos 1598dffb485Schristos int 1608dffb485Schristos exceptions_state_mc_action_iter_1 (void) 1618dffb485Schristos { 1628dffb485Schristos return exceptions_state_mc (CATCH_ITER_1); 1638dffb485Schristos } 1648dffb485Schristos 1658dffb485Schristos /* Return EXCEPTION to the nearest containing CATCH_SJLJ block. */ 1668dffb485Schristos 1678dffb485Schristos void 1688dffb485Schristos throw_exception_sjlj (const struct gdb_exception &exception) 1698dffb485Schristos { 1708dffb485Schristos /* Jump to the nearest CATCH_SJLJ block, communicating REASON to 1718dffb485Schristos that call via setjmp's return value. Note that REASON can't be 1728dffb485Schristos zero, by definition in common-exceptions.h. */ 1738dffb485Schristos exceptions_state_mc (CATCH_THROWING); 1748dffb485Schristos enum return_reason reason = exception.reason; 1758dffb485Schristos catchers.front ().exception = exception; 1768dffb485Schristos longjmp (catchers.front ().buf, reason); 1778dffb485Schristos } 1788dffb485Schristos 1798dffb485Schristos /* Implementation of throw_exception that uses C++ try/catch. */ 1808dffb485Schristos 1818dffb485Schristos void 1828dffb485Schristos throw_exception (gdb_exception &&exception) 1838dffb485Schristos { 1848dffb485Schristos if (exception.reason == RETURN_QUIT) 1858dffb485Schristos throw gdb_exception_quit (std::move (exception)); 186*5ba1f45fSchristos else if (exception.reason == RETURN_FORCED_QUIT) 187*5ba1f45fSchristos throw gdb_exception_forced_quit (std::move (exception)); 1888dffb485Schristos else if (exception.reason == RETURN_ERROR) 1898dffb485Schristos throw gdb_exception_error (std::move (exception)); 1908dffb485Schristos else 1918dffb485Schristos gdb_assert_not_reached ("invalid return reason"); 1928dffb485Schristos } 1938dffb485Schristos 1948dffb485Schristos static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) 1958dffb485Schristos throw_it (enum return_reason reason, enum errors error, const char *fmt, 1968dffb485Schristos va_list ap) 1978dffb485Schristos { 1988dffb485Schristos if (reason == RETURN_QUIT) 1998dffb485Schristos throw gdb_exception_quit (fmt, ap); 200*5ba1f45fSchristos else if (reason == RETURN_FORCED_QUIT) 201*5ba1f45fSchristos throw gdb_exception_forced_quit (fmt, ap); 2028dffb485Schristos else if (reason == RETURN_ERROR) 2038dffb485Schristos throw gdb_exception_error (error, fmt, ap); 2048dffb485Schristos else 2058dffb485Schristos gdb_assert_not_reached ("invalid return reason"); 2068dffb485Schristos } 2078dffb485Schristos 2088dffb485Schristos void 2098dffb485Schristos throw_verror (enum errors error, const char *fmt, va_list ap) 2108dffb485Schristos { 2118dffb485Schristos throw_it (RETURN_ERROR, error, fmt, ap); 2128dffb485Schristos } 2138dffb485Schristos 2148dffb485Schristos void 2158dffb485Schristos throw_vquit (const char *fmt, va_list ap) 2168dffb485Schristos { 2178dffb485Schristos throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap); 2188dffb485Schristos } 2198dffb485Schristos 2208dffb485Schristos void 2218dffb485Schristos throw_error (enum errors error, const char *fmt, ...) 2228dffb485Schristos { 2238dffb485Schristos va_list args; 2248dffb485Schristos 2258dffb485Schristos va_start (args, fmt); 2268dffb485Schristos throw_verror (error, fmt, args); 2278dffb485Schristos va_end (args); 2288dffb485Schristos } 2298dffb485Schristos 2308dffb485Schristos void 2318dffb485Schristos throw_quit (const char *fmt, ...) 2328dffb485Schristos { 2338dffb485Schristos va_list args; 2348dffb485Schristos 2358dffb485Schristos va_start (args, fmt); 2368dffb485Schristos throw_vquit (fmt, args); 2378dffb485Schristos va_end (args); 2388dffb485Schristos } 239*5ba1f45fSchristos 240*5ba1f45fSchristos void 241*5ba1f45fSchristos throw_forced_quit (const char *fmt, ...) 242*5ba1f45fSchristos { 243*5ba1f45fSchristos va_list args; 244*5ba1f45fSchristos 245*5ba1f45fSchristos va_start (args, fmt); 246*5ba1f45fSchristos throw_it (RETURN_FORCED_QUIT, GDB_NO_ERROR, fmt, args); 247*5ba1f45fSchristos va_end (args); 248*5ba1f45fSchristos } 249