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

Reply via email to