https://gcc.gnu.org/g:eab1653a1202027ec3111b239381ed97858d223f
commit eab1653a1202027ec3111b239381ed97858d223f Author: Alexandre Oliva <[email protected]> Date: Sat Jun 27 00:37:01 2026 -0300 GNAT exceptions in C++ This patch introduces __gnu_cxx::__gnat_exception, a class derived from abi::__foreign_exception, that can be used to catch GNAT exceptions and obtain information from them, such as the exception name and message: try { try { raise_gnat_exception (1); } catch (__gnu_cxx::__gnat_exception& e) { __builtin_printf("caught gnat %p\n", &e); __builtin_printf("what: %s\n", e.what ()); auto msg = e.__msg (); __builtin_printf("msg: %*s\n", msg.__len, msg.__ptr); throw; } } catch (abi::__foreign_exception& f) { __builtin_printf("caught foreign %p\n", &f); } prints: caught gnat 0x<_Unwind_Exception*> what: <GNAT Exception name> msg: <GNAT Exception message> caught foreign 0x<_Unwind_Exception*> I'm not entirely happy with the C++ API WRT how internal Ada strings are exposed as {len,ptr}, but something like std::string is probably undersirable in libsupc++, more so because it would require a hosted libstdc++. It would be cool to expose GNAT exceptions' backtraces as well, but System.Params.Default_Exception_Msg_Max_Length might conceivably vary, and that would offset the backtrace data, and we presumably don't want libstdc++ to depend on libgnat to get runtime access to that param and behave accordingly, and a #define that might go out of sync feels risky. for libstdc++-v3/ChangeLog * config/abi/pre/gnu-versioned-namespace.ver (CXXABI_2.0): Drop duplicates. Add __gnat_exception symbols. * config/abi/pre/gnu.ver (CXXABI_1.3.18): New. Add __gnat_exception symbols. * libsupc++/Makefile.am (sources): Add gnat_exception.cc. * libsupc++/Makefile.in: Rebuild. * libsupc++/cxxabi.h (__gnu_cxx::__gnat_exception): New. (__cxxabiv1::__foreign_exception): Befriend it. * libsupc++/eh_personality.cc (PERSONALITY_FUNCTION): Select __gnat_exception for GNAT exceptions. * libsupc++/eh_arm.cc (__cxa_type_match): Likewise. * libsupc++/eh_catch.cc (__cxxabiv1::__cxa_begin_catch): Return unwindHeader for GNAT exceptions. * libsupc++/gnat_exception.cc: New. * libsupc++/unwind-cxx.h (__is_gnat_exception_class): New, for __ARM_EABI_UNWINDER__ and generic. (__gnat_exception_class) [!__ARM_EABI_UNWINDER__]: New. Diff: --- .../config/abi/pre/gnu-versioned-namespace.ver | 7 +- libstdc++-v3/config/abi/pre/gnu.ver | 10 +++ libstdc++-v3/libsupc++/Makefile.am | 1 + libstdc++-v3/libsupc++/Makefile.in | 5 +- libstdc++-v3/libsupc++/cxxabi.h | 16 ++++ libstdc++-v3/libsupc++/eh_arm.cc | 6 +- libstdc++-v3/libsupc++/eh_catch.cc | 5 ++ libstdc++-v3/libsupc++/eh_personality.cc | 2 + libstdc++-v3/libsupc++/gnat_exception.cc | 91 ++++++++++++++++++++++ libstdc++-v3/libsupc++/unwind-cxx.h | 31 ++++++++ 10 files changed, 169 insertions(+), 5 deletions(-) diff --git a/libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver b/libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver index 7c5059944272..6298a9ddeb28 100644 --- a/libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver +++ b/libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver @@ -286,8 +286,6 @@ CXXABI_2.0 { _ZTIN10__cxxabiv121__vmi_class_type_infoE; _ZTIN10__cxxabiv115__forced_unwindE; _ZTIN10__cxxabiv119__foreign_exceptionE; - _ZTIN10__cxxabiv115__forced_unwindE; - _ZTIN10__cxxabiv119__foreign_exceptionE; # typeinfo name _ZTS[a-z]; @@ -372,6 +370,11 @@ CXXABI_2.0 { _ZdaPvSt11align_val_t; _ZdaPvSt11align_val_tRKSt9nothrow_t; _ZdaPv[jmy]St11align_val_t; + + # __gnu_cxx::__gnat_exception typeinfo and member functions + _ZNK9__gnu_cxx16__gnat_exception*; + _ZTIN9__gnu_cxx16__gnat_exceptionE; + _ZTSN9__gnu_cxx16__gnat_exceptionE; }; # Symbols in the support library (libsupc++) supporting trans-mem. diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 3a6afac83087..ab26420f9a67 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2999,6 +2999,16 @@ CXXABI_1.3.17 { } CXXABI_1.3.15; #endif +CXXABI_1.3.18 { + + global: + # __gnu_cxx::__gnat_exception typeinfo and member functions + _ZNK9__gnu_cxx16__gnat_exception*; + _ZTIN9__gnu_cxx16__gnat_exceptionE; + _ZTSN9__gnu_cxx16__gnat_exceptionE; + +} CXXABI_1.3.17; + # Symbols in the support library (libsupc++) supporting transactional memory. CXXABI_TM_1 { diff --git a/libstdc++-v3/libsupc++/Makefile.am b/libstdc++-v3/libsupc++/Makefile.am index a3dc60f39458..3523366fc13f 100644 --- a/libstdc++-v3/libsupc++/Makefile.am +++ b/libstdc++-v3/libsupc++/Makefile.am @@ -80,6 +80,7 @@ sources = \ enum_type_info.cc \ function_type_info.cc \ fundamental_type_info.cc \ + gnat_exception.cc \ guard.cc \ guard_error.cc \ hash_bytes.cc \ diff --git a/libstdc++-v3/libsupc++/Makefile.in b/libstdc++-v3/libsupc++/Makefile.in index db5e83803d52..ec4dd4af3d29 100644 --- a/libstdc++-v3/libsupc++/Makefile.in +++ b/libstdc++-v3/libsupc++/Makefile.in @@ -161,8 +161,8 @@ am__objects_1 = array_type_info.lo atexit_arm.lo atexit_thread.lo \ eh_catch.lo eh_exception.lo eh_globals.lo eh_personality.lo \ eh_ptr.lo eh_term_handler.lo eh_terminate.lo eh_tm.lo \ eh_throw.lo eh_type.lo eh_unex_handler.lo enum_type_info.lo \ - function_type_info.lo fundamental_type_info.lo guard.lo \ - guard_error.lo hash_bytes.lo nested_exception.lo \ + function_type_info.lo fundamental_type_info.lo gnat_exception.lo \ + guard.lo guard_error.lo hash_bytes.lo nested_exception.lo \ new_handler.lo new_op.lo new_opnt.lo new_opv.lo new_opvnt.lo \ new_opa.lo new_opant.lo new_opva.lo new_opvant.lo del_opa.lo \ del_opant.lo del_opsa.lo del_opva.lo del_opvant.lo \ @@ -546,6 +546,7 @@ sources = \ enum_type_info.cc \ function_type_info.cc \ fundamental_type_info.cc \ + gnat_exception.cc \ guard.cc \ guard_error.cc \ hash_bytes.cc \ diff --git a/libstdc++-v3/libsupc++/cxxabi.h b/libstdc++-v3/libsupc++/cxxabi.h index 244b21fc9db3..09e48bd2a866 100644 --- a/libstdc++-v3/libsupc++/cxxabi.h +++ b/libstdc++-v3/libsupc++/cxxabi.h @@ -54,6 +54,10 @@ #include <bits/cxxabi_init_exception.h> #ifdef __cplusplus +namespace __gnu_cxx +{ + class __gnat_exception; +} namespace __cxxabiv1 { extern "C" @@ -662,6 +666,7 @@ namespace __cxxabiv1 { virtual ~__foreign_exception() throw(); virtual void __pure_dummy() = 0; // prevent catch by value + friend class __gnu_cxx::__gnat_exception; }; } // namespace __cxxabiv1 @@ -690,6 +695,17 @@ namespace abi = __cxxabiv1; namespace __gnu_cxx { + // A magic placeholder class that can be caught by reference + // to recognize GNAT exceptions. + class __gnat_exception : public abi::__foreign_exception + { + virtual ~__gnat_exception() throw(); + public: + const char *what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW; + struct __ada_str { unsigned __len; const char *__ptr; }; + __ada_str __msg() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW; + }; + /** * @brief Exception thrown by __cxa_guard_acquire. * @ingroup exceptions diff --git a/libstdc++-v3/libsupc++/eh_arm.cc b/libstdc++-v3/libsupc++/eh_arm.cc index 7008ce60f6df..5d0ed1d789fe 100644 --- a/libstdc++-v3/libsupc++/eh_arm.cc +++ b/libstdc++-v3/libsupc++/eh_arm.cc @@ -56,7 +56,11 @@ __cxa_type_match(_Unwind_Exception* ue_header, if (forced_unwind) throw_type = &typeid(abi::__forced_unwind); else if (foreign_exception) - throw_type = &typeid(abi::__foreign_exception); + { + throw_type = &typeid(abi::__foreign_exception); + if (__is_gnat_exception_class(ue_header->exception_class)) + throw_type = &typeid(__gnu_cxx::__gnat_exception); + } else { if (dependent_exception) diff --git a/libstdc++-v3/libsupc++/eh_catch.cc b/libstdc++-v3/libsupc++/eh_catch.cc index 424dbc724b6a..650c75e177d0 100644 --- a/libstdc++-v3/libsupc++/eh_catch.cc +++ b/libstdc++-v3/libsupc++/eh_catch.cc @@ -58,6 +58,11 @@ __cxxabiv1::__cxa_begin_catch (void *exc_obj_in) _GLIBCXX_NOTHROW // Remember for end_catch and rethrow. globals->caughtExceptions = header; + // For GNAT exceptions, return the address of the unwind header. + // __gnat_exception member functions rely on that. + if (__is_gnat_exception_class(header->unwindHeader.exception_class)) + return &header->unwindHeader; + // ??? No sensible value to return; we don't know what the // object is, much less where it is in relation to the header. return 0; diff --git a/libstdc++-v3/libsupc++/eh_personality.cc b/libstdc++-v3/libsupc++/eh_personality.cc index 10458318f88e..ff242872d658 100644 --- a/libstdc++-v3/libsupc++/eh_personality.cc +++ b/libstdc++-v3/libsupc++/eh_personality.cc @@ -571,6 +571,8 @@ PERSONALITY_FUNCTION (int version, else if (foreign_exception) { throw_type = &typeid(abi::__foreign_exception); + if (__is_gnat_exception_class(exception_class)) + throw_type = &typeid(__gnu_cxx::__gnat_exception); } else #endif diff --git a/libstdc++-v3/libsupc++/gnat_exception.cc b/libstdc++-v3/libsupc++/gnat_exception.cc new file mode 100644 index 000000000000..a1b6d1ee71fc --- /dev/null +++ b/libstdc++-v3/libsupc++/gnat_exception.cc @@ -0,0 +1,91 @@ +// -*- C++ -*- __gnu_cxx::__gnat_exception implementation. +// Copyright (C) 2026 Free Software Foundation, Inc. +// +// This file is part of GCC. +// +// GCC is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3, or (at your option) +// any later version. +// +// GCC is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +#include "exception" +#include <cxxabi.h> +#include "unwind.h" + +namespace { + +#if __ARM_EABI_UNWINDER__ && ! __USING_SJLJ_EXCEPTIONS__ + typedef _Unwind_Control_Block GNAT_Unwind_Header; +#else + typedef struct + { + _Unwind_Exception_Class exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + _Unwind_Word private_[6]; + } GNAT_Unwind_Header; +#endif + + struct GNAT_Exception_Data + { + bool not_handled_by_others; + char lang; + unsigned name_length; + const char *full_name; /* NULL terminated string. */ + /* ... */ + }; + + struct GNAT_Exception_Occurrence + { + GNAT_Exception_Data *eid; + GNAT_Unwind_Header *header; + unsigned msg_length; + char msg[/*msg_[max_]length*/]; + /* ... */ + }; + + struct GNAT_Exception + { + GNAT_Unwind_Header header; + GNAT_Exception_Occurrence occurrence; + }; +} + +__gnu_cxx::__gnat_exception::~__gnat_exception() throw() +{ +} + +const char* +__gnu_cxx::__gnat_exception::what() + const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT +{ + GNAT_Exception const *ex = reinterpret_cast<GNAT_Exception const *>(this); + if (ex->occurrence.eid) + return ex->occurrence.eid->full_name; + return "GNAT exception"; +} + +struct __gnu_cxx::__gnat_exception::__ada_str +__gnu_cxx::__gnat_exception::__msg() + const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT +{ + GNAT_Exception const *ex = reinterpret_cast<GNAT_Exception const *>(this); + __ada_str str = { + ex->occurrence.msg_length, + ex->occurrence.msg + }; + return str; +} diff --git a/libstdc++-v3/libsupc++/unwind-cxx.h b/libstdc++-v3/libsupc++/unwind-cxx.h index b6030f0d1978..6e69c1ff9c5b 100644 --- a/libstdc++-v3/libsupc++/unwind-cxx.h +++ b/libstdc++-v3/libsupc++/unwind-cxx.h @@ -250,6 +250,20 @@ __is_gxx_exception_class(_Unwind_Exception_Class c) && (c[7] == '\0' || c[7] == '\x01'); } +static inline bool +__is_gnat_exception_class(_Unwind_Exception_Class c) +{ + // TODO: Take advantage of the fact that c will always be word aligned. + return c[0] == 'G' + && c[1] == 'N' + && c[2] == 'U' + && c[3] == '-' + && c[4] == 'A' + && c[5] == 'd' + && c[6] == 'a' + && c[7] == '\0'; +} + // Only checks for primary or dependent, but not that it is a C++ exception at // all. static inline bool @@ -346,6 +360,23 @@ __is_gxx_exception_class(_Unwind_Exception_Class c) || c == __gxx_dependent_exception_class; } +// GNAT reports exceptions as "GNU-Ada\0" +const _Unwind_Exception_Class __gnat_exception_class += ((((((((_Unwind_Exception_Class) 'G' + << 8 | (_Unwind_Exception_Class) 'N') + << 8 | (_Unwind_Exception_Class) 'U') + << 8 | (_Unwind_Exception_Class) '-') + << 8 | (_Unwind_Exception_Class) 'A') + << 8 | (_Unwind_Exception_Class) 'd') + << 8 | (_Unwind_Exception_Class) 'a') + << 8 | (_Unwind_Exception_Class) '\0'); + +static inline bool +__is_gnat_exception_class(_Unwind_Exception_Class c) +{ + return c == __gnat_exception_class; +} + // Only checks for primary or dependent, but not that it is a C++ exception at // all. static inline bool
