Hello, In the attachment I'm sending changes which address
__mingw_init_ehandler code issues and try to implement that
functionality also for arm32/arm64. The amd64 is same what I sent in
previous email, what I checked on example tests that is working.

The arm32 and arm64 changes are completely untested, I just extended
that amd64 code according to the published MS documentation.

So I would like to ask Evgeny for testing these changes and running
mingw-w64 test suite if it working or if there are any issues.
>From 01635c58e4b3fe1caf5f4b6c58cae1009bc1a651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Sat, 13 Jun 2026 11:31:36 +0200
Subject: [PATCH 1/3] crt: crt_handler: Rewrite __mingw_init_ehandler code used
 for amd64 non-SEH builds

The purpose of registering __mingw_SEH_error_handler is to dispatch C
signal exceptions and deliver them to C signal handlers. This done at the
application entry point. So it should not registered in any DLL module. And
also it should not be registered for any other function than the entry
point of EXE application.

The propagation of SEH exception from one function to another on the
backtrace is done via SEH unwind codes.

Function __mingw_init_ehandler() is used only for amd64 non-SEH builds and
is currently registering the SEH handler __mingw_SEH_error_handler for all
functions in EXE application and also for all functions in DLL module, both
via the RtlAddFunctionTable() call. This means that the exception is never
propagated to parent functions and is immediately processed, even when it
should not be.

Change this logic for amd64 non-SEH builds by registering SEH handler
__mingw_SEH_error_handler via RtlAddFunctionTable() only for
__tmainCRTStartup() entry point in EXE application at the exact same place
where __mingw_SEH_error_handler is registered for SEH builds.
Second change is to not register __mingw_SEH_error_handler for DLL modules
at all (like it is already for SEH builds). And for SEH exception
propagation register simple SEH unwind code which traverse backtrace via
the stack frame pointers %rbp. It is not perfect, but probably the best
what can be done for amd64 non-SEH builds. It should be reliable when
compiling mingw-w64 and also application code with -fno-omit-frame-pointer
flag. And when frame pointers are absent then %rbp should be untouched and
not used for temporary variables. This could be achieved by -ffixed-rbp.

To make code more modular, put this new code which calls RtlAddFunctionTable()
function into the new runtime_seh.h file.
---
 mingw-w64-crt/crt/crt_handler.c |  67 -----------
 mingw-w64-crt/crt/crtdll.c      |  28 ++++-
 mingw-w64-crt/crt/crtexe.c      |  28 ++++-
 mingw-w64-crt/crt/runtime_seh.h | 207 ++++++++++++++++++++++++++++++++
 4 files changed, 254 insertions(+), 76 deletions(-)
 create mode 100644 mingw-w64-crt/crt/runtime_seh.h

diff --git a/mingw-w64-crt/crt/crt_handler.c b/mingw-w64-crt/crt/crt_handler.c
index 191c2da65751..c484e8e57a8c 100644
--- a/mingw-w64-crt/crt/crt_handler.c
+++ b/mingw-w64-crt/crt/crt_handler.c
@@ -6,77 +6,10 @@
 
 #include <windows.h>
 #include <excpt.h>
-#include <string.h>
 #include <stdlib.h>
-#include <malloc.h>
-#include <memory.h>
-#include <signal.h>
-#include <stdio.h>
 
 EXCEPTION_DISPOSITION __cdecl __mingw_SEH_error_handler(struct 
_EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *);
 
-#if defined(__x86_64__) && !defined(_MSC_VER) && !defined(__SEH__)
-
-#pragma pack(push,1)
-typedef struct _UNWIND_INFO {
-  BYTE Version:3;
-  BYTE Flags:5;
-  BYTE PrologSize;
-  BYTE CountOfUnwindCodes;
-  BYTE FrameRegisterAndOffset;
-  ULONG AddressOfExceptionHandler;
-} UNWIND_INFO,*PUNWIND_INFO;
-#pragma pack(pop)
-
-PIMAGE_SECTION_HEADER _FindPESectionByName (const char *);
-PIMAGE_SECTION_HEADER _FindPESectionExec (size_t);
-PBYTE _GetPEImageBase (void);
-
-#define MAX_PDATA_ENTRIES 32
-static RUNTIME_FUNCTION emu_pdata[MAX_PDATA_ENTRIES];
-static UNWIND_INFO emu_xdata[MAX_PDATA_ENTRIES];
-
-int __mingw_init_ehandler (void);
-int
-__mingw_init_ehandler (void)
-{
-  static int was_here = 0;
-  size_t e = 0;
-  PIMAGE_SECTION_HEADER pSec;
-  PBYTE _ImageBase = _GetPEImageBase ();
-  
-  if (was_here || !_ImageBase)
-    return was_here;
-  was_here = 1;
-  if (_FindPESectionByName (".pdata") != NULL)
-    return 1;
-
-  e = 0;
-  /* Fill tables and entries.  */
-  while (e < MAX_PDATA_ENTRIES && (pSec = _FindPESectionExec (e)) != NULL)
-    {
-      emu_xdata[e].Version = 1;
-      emu_xdata[e].Flags = UNW_FLAG_EHANDLER;
-      emu_xdata[e].AddressOfExceptionHandler =
-       (DWORD)(size_t) ((LPBYTE)__mingw_SEH_error_handler - _ImageBase);
-      emu_pdata[e].BeginAddress = pSec->VirtualAddress;
-      emu_pdata[e].EndAddress = pSec->VirtualAddress + pSec->Misc.VirtualSize;
-      emu_pdata[e].UnwindData =
-       (DWORD)(size_t)((LPBYTE)&emu_xdata[e] - _ImageBase);
-      ++e;
-    }
-#ifdef _DEBUG_CRT
-  if (!e || e > MAX_PDATA_ENTRIES)
-    abort ();
-#endif
-  /* RtlAddFunctionTable.  */
-  if (e != 0)
-    RtlAddFunctionTable (emu_pdata, e, (DWORD64)_ImageBase);
-  return 1;
-}
-
-#endif
-
 #if defined(__i386__)
 /* We need to make sure that we align the stack to 16 bytes for the sake of 
SSE */
 __attribute__((force_align_arg_pointer))
diff --git a/mingw-w64-crt/crt/crtdll.c b/mingw-w64-crt/crt/crtdll.c
index 18e831ef9525..2ae36d75fcd5 100644
--- a/mingw-w64-crt/crt/crtdll.c
+++ b/mingw-w64-crt/crt/crtdll.c
@@ -17,9 +17,14 @@
 #include <sect_attribs.h>
 #include <locale.h>
 
-#if defined(__x86_64__) && !defined(__SEH__)
-extern int __mingw_init_ehandler (void);
+#if defined(__SEH__) && (!defined(__clang__) || __clang_major__ >= 7)
+#define SEH_INLINE_ASM
 #endif
+
+#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+#include "runtime_seh.h"
+#endif
+
 extern void __main ();
 extern void _pei386_runtime_relocator (void);
 extern _PIFV __xi_a[];
@@ -72,9 +77,6 @@ WINBOOL WINAPI _CRT_INIT (HANDLE hDllHandle, DWORD dwReason, 
LPVOID lpreserved)
          __native_startup_state = __initializing;
          
          _pei386_runtime_relocator ();
-#if defined(__x86_64__) && !defined(__SEH__)
-         __mingw_init_ehandler ();
-#endif
          ret = _initialize_onexit_table (&atexit_table);
          if (ret != 0)
            goto i__leave;
@@ -146,6 +148,14 @@ DllMainCRTStartup (HANDLE hDllHandle, DWORD dwReason, 
LPVOID lpreserved)
 {
   WINBOOL retcode = TRUE;
 
+#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+  if (dwReason == DLL_PROCESS_ATTACH)
+    {
+      if (!register_runtime_image_unwind_info ()) /* dynamically register 
propagation of SEH exceptions for the whole image */
+        return FALSE;
+    }
+#endif
+
   if (dwReason == DLL_PROCESS_ATTACH)
     __mingw_app_type = 0;
 
@@ -174,6 +184,14 @@ DllMainCRTStartup (HANDLE hDllHandle, DWORD dwReason, 
LPVOID lpreserved)
     }
 i__leave:
   __native_dllmain_reason = UINT_MAX;
+
+#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+  if (dwReason == DLL_PROCESS_DETACH || (dwReason == DLL_PROCESS_ATTACH && 
!retcode))
+    {
+      unregister_runtime_image_unwind_info (); /* dynamically unregister 
unwind info when unloading the DLL library */
+    }
+#endif
+
   return retcode ;
 }
 
diff --git a/mingw-w64-crt/crt/crtexe.c b/mingw-w64-crt/crt/crtexe.c
index 86c3842f6162..c17c5b66cfad 100644
--- a/mingw-w64-crt/crt/crtexe.c
+++ b/mingw-w64-crt/crt/crtexe.c
@@ -33,6 +33,10 @@
 #endif
 #endif
 
+#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+#include "runtime_seh.h"
+#endif
+
 extern IMAGE_DOS_HEADER __ImageBase;
 
 int *__cdecl __p__commode(void);
@@ -67,9 +71,6 @@ static int has_cctor = 0;
 
 extern void _pei386_runtime_relocator (void);
 EXCEPTION_DISPOSITION __cdecl __mingw_SEH_error_handler (struct 
_EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *);
-#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
-int __mingw_init_ehandler (void);
-#endif
 static int duplicate_ppstrings (int ac, _TCHAR ***av);
 
 extern int _MINGW_INSTALL_DEBUG_MATHERR;
@@ -146,12 +147,19 @@ int mainCRTStartup (void)
   return __tmainCRTStartup ();
 }
 
+#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+static int __tmainCRTStartup_real(void);
+static const char __tmainCRTStartup_end;
+#endif
 static
 #if defined(__i386__) || defined(_X86_)
 /* We need to make sure that we align the stack to 16 bytes for the sake of SSE
    opts in main or in functions called from main.  */
 __attribute__((force_align_arg_pointer))
 #endif
+#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+__attribute__((section(".text.__tmainCRTStartup$A")))
+#endif
 __declspec(noinline) int
 __tmainCRTStartup (void)
 {
@@ -166,7 +174,19 @@ __tmainCRTStartup (void)
 #elif defined(SEH_INLINE_ASM)
     asm volatile (".seh_handler " ASM_SEH_PREFIX "%c0" ASM_SEH_SUFFIX ", " 
ASM_SEH_EXCEPT :: "i" (__mingw_SEH_error_handler)); /* statically register SEH 
error handler, it is active only in the current function */
 #elif defined(__x86_64__)
-    __mingw_init_ehandler (); /* dynamically register SEH error handler for 
all functions, it is active until program terminates */
+    asm volatile ("" :: "r" (__builtin_frame_address (0))); /* force usage of 
the standard prolog with frame pointer */
+    if (!register_runtime_function_exception_handler 
((uintptr_t)&__tmainCRTStartup, (uintptr_t)&__tmainCRTStartup_end, 
__mingw_SEH_error_handler)) /* dynamically register SEH error handler, it is 
active only in the specified function */
+      _amsg_exit (22); /* _RT_NONCONT_TXT */
+    if (!register_runtime_image_unwind_info ()) /* dynamically register 
propagation of SEH exceptions for the whole image */
+      _amsg_exit (22); /* _RT_NONCONT_TXT */
+    int ret = __tmainCRTStartup_real (); /* call the real __tmainCRTStartup 
with a new frame */
+    asm volatile ("nop"); /* required by SEH unwind for __tmainCRTStartup_real 
*/
+    return ret;
+}
+__attribute__((section(".text.__tmainCRTStartup$Z"))) static const char 
__tmainCRTStartup_end = 0;
+static __declspec(noinline) int __tmainCRTStartup_real (void)
+{
+    asm volatile ("" :: "r" (__builtin_frame_address (0))); /* force usage of 
the standard prolog with frame pointer */
 #else
 #error unsupported platform
 #endif
diff --git a/mingw-w64-crt/crt/runtime_seh.h b/mingw-w64-crt/crt/runtime_seh.h
new file mode 100644
index 000000000000..29703978cbdd
--- /dev/null
+++ b/mingw-w64-crt/crt/runtime_seh.h
@@ -0,0 +1,207 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <windows.h>
+
+#if defined(__x86_64__)
+
+/* 
https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#unwind-data-definitions-in-c
 */
+
+typedef enum {
+  UWOP_PUSH_NONVOL = 0,  /* info == register number */
+  UWOP_ALLOC_LARGE,      /* no info, alloc size in next 2 slots */
+  UWOP_ALLOC_SMALL,      /* info == size of allocation / 8 - 1 */
+  UWOP_SET_FPREG,        /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+  UWOP_SAVE_NONVOL,      /* info == register number, offset in next slot */
+  UWOP_SAVE_NONVOL_FAR,  /* info == register number, offset in next 2 slots */
+  UWOP_SAVE_XMM = 6,     /* info == XMM reg number, offset in next slot, only 
for Version=1 */
+  UWOP_EPILOG = 6,       /* info&1 == 1: CodeOffset is both size and offset, 
0: CodeOffset is size and offset in next slot, only for Version=2 */
+  UWOP_SAVE_XMM_FAR = 7, /* info == XMM reg number, offset in next 2 slots, 
only for Version=1 */
+  UWOP_SPARE = 7,        /* no info, spare next 2 slots, only for Version=2 */
+  UWOP_SAVE_XMM128,      /* info == XMM reg number, offset in next slot */
+  UWOP_SAVE_XMM128_FAR,  /* info == XMM reg number, offset in next 2 slots */
+  UWOP_PUSH_MACHFRAME,   /* info == 0: no error-code, 1: error-code */
+} UNWIND_CODE_OPS;
+
+typedef enum {
+  UWREG_RAX = 0,
+  UWREG_RCX,
+  UWREG_RDX,
+  UWREG_RBX,
+  UWREG_RSP,
+  UWREG_RBP,
+  UWREG_RSI,
+  UWREG_RDI,
+  UWREG_R8,
+  UWREG_R9,
+  UWREG_R10,
+  UWREG_R11,
+  UWREG_R12,
+  UWREG_R13,
+  UWREG_R14,
+  UWREG_R15,
+  UWREG_XMM0,
+  UWREG_XMM1,
+  UWREG_XMM2,
+  UWREG_XMM3,
+  UWREG_XMM4,
+  UWREG_XMM5,
+  UWREG_XXM6,
+  UWREG_XMM7,
+  UWREG_XMM8,
+  UWREG_XMM9,
+  UWREG_XMM10,
+  UWREG_XMM11,
+  UWREG_XMM12,
+  UWREG_XMM13,
+  UWREG_XXM14,
+  UWREG_XMM15,
+} UNWIND_REGS;
+
+typedef struct {
+  BYTE CodeOffset;
+  BYTE UnwindOp:4;
+  BYTE OpInfo:4;
+} UNWIND_CODE;
+
+#define UNWIND_INFO(unwind_codes_count, exception_data_count) struct 
__attribute__((aligned(4))) { \
+  BYTE Version:3; \
+  BYTE Flags:5; \
+  BYTE SizeOfProlog; \
+  BYTE CountOfCodes; /* must match unwind_codes_count */ \
+  BYTE FrameRegister:4; \
+  BYTE FrameOffset:4; \
+  UNWIND_CODE UnwindCode[unwind_codes_count]; \
+  /* if unwind_codes_count is odd number then there is an automatic padding */ 
\
+  union __attribute__((aligned(4))) { \
+    ULONG ExceptionHandler; /* when Flags do not contain UNW_FLAG_CHAININFO */ 
\
+    ULONG FunctionEntry; /* when Flags contains UNW_FLAG_CHAININFO */ \
+  }; \
+  ULONG ExceptionData[exception_data_count]; \
+}
+
+/* Define unwind code for standard prolog which uses frame pointers via rbp.
+ * This makes the RtlUnwind() function to work and allows to traverse stack.
+ *
+ * As the one unwind code is defined for the whole image, it means that it
+ * does not represent one function and so there is no real function prolog.
+ * Therefore SizeOfProlog is 0 and all CodeOffset are also zeros.
+ *
+ * Functions which do not create its own frame pointer on stack (for example
+ * when compiling with -fomit-frame-pointer) are hidden during unwinding.
+ * Startup code for mingw-w64 manually ensures that frame pointers via rbp
+ * are properly maintained. Rest is up to the application. If application
+ * is compiled with -fomit-frame-pointer then it is expected that rbp value
+ * is not damaged or used for some intermediate operations. This can be
+ * achieved by additional application compile flag -ffixed-rbp.
+ */
+#define UNWIND_CODE_TABLE { \
+    { .UnwindOp = UWOP_SET_FPREG,                       }, /* movq %rbp, %rsp 
*/ \
+    { .UnwindOp = UWOP_PUSH_NONVOL, .OpInfo = UWREG_RBP }, /* popq %rbp */ \
+}
+#define UNWIND_CODE_TABLE_COUNT 
(sizeof((UNWIND_CODE[])UNWIND_CODE_TABLE)/sizeof(UNWIND_CODE))
+
+#else
+
+#error unsupported platform
+
+#endif
+
+
+
+#if defined(__x86_64__)
+static const UNWIND_INFO(UNWIND_CODE_TABLE_COUNT, 0) 
all_functions_unwind_xdata = {
+  .Version = 1,
+  .CountOfCodes = UNWIND_CODE_TABLE_COUNT,
+  .FrameRegister = UWREG_RBP,
+  .UnwindCode = UNWIND_CODE_TABLE,
+};
+#else
+#error unsupported platform
+#endif
+
+static RUNTIME_FUNCTION all_functions_pdata = {
+  .BeginAddress = 0,
+#if defined(__x86_64__)
+  .EndAddress = -1, /* filled at runtime */
+#endif
+  .UnwindData = -1, /* filled at runtime */
+};
+
+/* Registration of unwind info for the whole image must be done _after_
+ * registering unwind info for separate functions or registering SEH handlers.
+ * It is because this registration covers whole image and covers also ranges of
+ * other functions. RtlUnwind() searches for unwind info first in executable
+ * image and then in runtime tables in order in which they were registered.
+ */
+static inline __attribute__((always_inline)) int 
register_runtime_image_unwind_info(void)
+{
+  /* All addresses in UNWIND_INFO and RUNTIME_FUNCTION are relative the image 
base. */
+  extern IMAGE_DOS_HEADER __ImageBase;
+  const DWORD image_size = ((IMAGE_NT_HEADERS*)((BYTE*)&__ImageBase + 
__ImageBase.e_lfanew))->OptionalHeader.SizeOfImage;
+#if defined(__x86_64__)
+  all_functions_pdata.EndAddress = image_size;
+#endif
+  all_functions_pdata.UnwindData = (uintptr_t)&all_functions_unwind_xdata - 
(uintptr_t)&__ImageBase;
+  return RtlAddFunctionTable(&all_functions_pdata, 1, (uintptr_t)&__ImageBase);
+}
+
+/* It is required to unregister unwind info when unloading the image.
+ * This is mostly for cases when DLL library is being unloaded.
+ */
+static inline __attribute__((always_inline)) void 
unregister_runtime_image_unwind_info(void)
+{
+  RtlDeleteFunctionTable(&all_functions_pdata);
+}
+
+
+
+#if defined(__x86_64__)
+static UNWIND_INFO(UNWIND_CODE_TABLE_COUNT, 0) function_unwind_xdata = {
+  .Version = 1,
+  .Flags = UNW_FLAG_EHANDLER,
+  .CountOfCodes = UNWIND_CODE_TABLE_COUNT,
+  .FrameRegister = UWREG_RBP,
+  .UnwindCode = UNWIND_CODE_TABLE,
+  .ExceptionHandler = -1, /* filled at runtime */
+};
+#else
+#error unsupported platform
+#endif
+
+static RUNTIME_FUNCTION function_pdata = {
+  .BeginAddress = -1, /* filled at runtime */
+#if defined(__x86_64__)
+  .EndAddress = -1, /* filled at runtime */
+#endif
+  .UnwindData = -1, /* filled at runtime */
+};
+
+/* Function for which is being registered SEH exception handler needs to use
+ * standard prolog and also direct child functions need to use standard prolog.
+ * Registration of SEH handler for particular function needs to be done before
+ * calling register_runtime_unwind_info(). Otherwise it would have no effect.
+ * Also please note that without the followup register_runtime_unwind_info()
+ * call, the SEH exception would not be propagated to SEH handler because
+ * RtlUnwind() would not be able to unwind stack and find this SEH handler.
+ */
+static inline __attribute__((always_inline)) int 
register_runtime_function_exception_handler(uintptr_t function_begin, uintptr_t 
function_end, PEXCEPTION_ROUTINE exception_handler)
+{
+  /* All addresses in UNWIND_INFO and RUNTIME_FUNCTION are relative the image 
base. */
+  extern IMAGE_DOS_HEADER __ImageBase;
+  function_unwind_xdata.ExceptionHandler = (uintptr_t)exception_handler - 
(uintptr_t)&__ImageBase;
+  function_pdata.BeginAddress = function_begin - (uintptr_t)&__ImageBase;
+#if defined(__x86_64__)
+  function_pdata.EndAddress = function_end - (uintptr_t)&__ImageBase;
+#endif
+  function_pdata.UnwindData = (uintptr_t)&function_unwind_xdata - 
(uintptr_t)&__ImageBase;
+  return RtlAddFunctionTable(&function_pdata, 1, (uintptr_t)&__ImageBase);
+}
+
+static inline __attribute__((always_inline)) void 
unregister_runtime_function_exception_handler(void)
+{
+  RtlDeleteFunctionTable(&function_pdata);
+}
-- 
2.20.1


>From 85093568e03116c290ede6e56eab45e07e301404 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Wed, 17 Jun 2026 23:39:05 +0200
Subject: [PATCH 2/3] crt: test: Adjust t_sigfpe.c to work for non-SEH builds

Every (callback) function compiled by non-SEH build which is called from
CRT library needs to have a standard prolog with its own frame pointer
and also standard epilog. This is needed to ensure that CRT library would
see our function as not continuation of its function. Therefore prevent
tail optimization and use __builtin_frame_address(0) which forces compiler
to register prolog with frame pointer even when -fomit-frame-pointer is used.

With previous and this change, the t_sigfpe.c test compiled by amd64
non-SEH gcc version is passing.
---
 mingw-w64-crt/testcases/t_sigfpe.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/mingw-w64-crt/testcases/t_sigfpe.c 
b/mingw-w64-crt/testcases/t_sigfpe.c
index 0c30621052c1..bc9ab024568c 100644
--- a/mingw-w64-crt/testcases/t_sigfpe.c
+++ b/mingw-w64-crt/testcases/t_sigfpe.c
@@ -1162,8 +1162,15 @@ __attribute__((force_align_arg_pointer))
 #endif
 static unsigned __stdcall test_from_beginthreadex(void *arg)
 {
+#if !defined(__i386__) && !defined(__SEH__)
+  asm volatile ("" :: "r" (__builtin_frame_address(0))); /* force usage of the 
standard prolog with frame pointer */
+#endif
   (void)arg;
-  return test();
+  int ret = test();
+#if !defined(__i386__) && !defined(__SEH__)
+  asm volatile (""); /* prevent tail optimization */
+#endif
+  return ret;
 }
 
 #if defined(__i386__)
@@ -1180,6 +1187,9 @@ static void __cdecl test_from_beginthread(void *arg)
    * The thread handle is used by _beginthread()'s caller which will close it.
    * So the only safe option is to exit this tread via WinAPI ExitThread() 
function.
    */
+#if !defined(__i386__) && !defined(__SEH__)
+  asm volatile ("" :: "r" (__builtin_frame_address(0))); /* force usage of the 
standard prolog with frame pointer */
+#endif
   (void)arg;
   ExitThread(test());
 }
-- 
2.20.1


>From 1802c7344e53c7ca790386abee79ddd48e6c6e70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Sat, 20 Jun 2026 16:41:23 +0200
Subject: [PATCH 3/3] WIP: UNTESTED: crt: crt_handler: Extend SEH handler
 registration via RtlAddFunctionTable() for arm32 and arm64

gcc toolchain for arm64 platform currently does not support generating SEH
code and registering SEH exception handlers. Therefore for these toolchains
is needed fallback code via RtlAddFunctionTable() like it is there for amd64.
arm32 and arm64 platforms have slightly different UNWIND_DATA (.xdata)
structure than the existing amd64.
---
 mingw-w64-crt/crt/crtexe.c      | 10 ++--
 mingw-w64-crt/crt/runtime_seh.h | 84 +++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+), 6 deletions(-)

diff --git a/mingw-w64-crt/crt/crtexe.c b/mingw-w64-crt/crt/crtexe.c
index c17c5b66cfad..c52ac02ac214 100644
--- a/mingw-w64-crt/crt/crtexe.c
+++ b/mingw-w64-crt/crt/crtexe.c
@@ -33,7 +33,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+#if !defined(__i386__) && !defined(SEH_INLINE_ASM)
 #include "runtime_seh.h"
 #endif
 
@@ -147,7 +147,7 @@ int mainCRTStartup (void)
   return __tmainCRTStartup ();
 }
 
-#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+#if !defined(__i386__) && !defined(SEH_INLINE_ASM)
 static int __tmainCRTStartup_real(void);
 static const char __tmainCRTStartup_end;
 #endif
@@ -157,7 +157,7 @@ static
    opts in main or in functions called from main.  */
 __attribute__((force_align_arg_pointer))
 #endif
-#if defined(__x86_64__) && !defined(SEH_INLINE_ASM)
+#if !defined(__i386__) && !defined(SEH_INLINE_ASM)
 __attribute__((section(".text.__tmainCRTStartup$A")))
 #endif
 __declspec(noinline) int
@@ -173,7 +173,7 @@ __tmainCRTStartup (void)
     __writefsdword (0, (DWORD)&exception_record); /* dynamically register SEH 
error handler, it is active until manually unregistered */
 #elif defined(SEH_INLINE_ASM)
     asm volatile (".seh_handler " ASM_SEH_PREFIX "%c0" ASM_SEH_SUFFIX ", " 
ASM_SEH_EXCEPT :: "i" (__mingw_SEH_error_handler)); /* statically register SEH 
error handler, it is active only in the current function */
-#elif defined(__x86_64__)
+#else
     asm volatile ("" :: "r" (__builtin_frame_address (0))); /* force usage of 
the standard prolog with frame pointer */
     if (!register_runtime_function_exception_handler 
((uintptr_t)&__tmainCRTStartup, (uintptr_t)&__tmainCRTStartup_end, 
__mingw_SEH_error_handler)) /* dynamically register SEH error handler, it is 
active only in the specified function */
       _amsg_exit (22); /* _RT_NONCONT_TXT */
@@ -187,8 +187,6 @@ __attribute__((section(".text.__tmainCRTStartup$Z"))) 
static const char __tmainC
 static __declspec(noinline) int __tmainCRTStartup_real (void)
 {
     asm volatile ("" :: "r" (__builtin_frame_address (0))); /* force usage of 
the standard prolog with frame pointer */
-#else
-#error unsupported platform
 #endif
 
     void *lock_free = NULL;
diff --git a/mingw-w64-crt/crt/runtime_seh.h b/mingw-w64-crt/crt/runtime_seh.h
index 29703978cbdd..f5c34f3b39ea 100644
--- a/mingw-w64-crt/crt/runtime_seh.h
+++ b/mingw-w64-crt/crt/runtime_seh.h
@@ -104,6 +104,68 @@ typedef struct {
 }
 #define UNWIND_CODE_TABLE_COUNT 
(sizeof((UNWIND_CODE[])UNWIND_CODE_TABLE)/sizeof(UNWIND_CODE))
 
+#elif defined(__arm__) || defined(__aarch64__)
+
+/* 
https://learn.microsoft.com/en-us/cpp/build/arm-exception-handling#xdata-records
 */
+/* 
https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling#xdata-records
 */
+/* IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA */
+
+#ifdef __arm__
+/* TODO arm32 UNWIND_CODE_OPS enum */
+#else /* __aarch64__ */
+/* This enum is not complete, it contains only opcodes used in the code below. 
*/
+typedef enum {
+  UWOP_SAVE_FPLR_X = 0x80,
+  UWOP_SET_FP = 0xE1,
+  UWOP_END = 0xE4,
+} UNWIND_CODE_OPS;
+#define UWOP_SAVE_FPLR_X(size) (UWOP_SAVE_FPLR_X + (size)/8-1)
+#endif
+
+#ifdef __arm__
+#define DECLARE_FunctionFragment DWORD FunctionFragment:1
+#define CODE_WORDS_BITS 4
+#else /* __aarch64__ */
+#define DECLARE_FunctionFragment /* FunctionFragment is not present */
+#define CODE_WORDS_BITS 5
+#endif
+
+#define UNWIND_INFO(epilog_count, unwind_codes_size, exception_data_count) 
struct __attribute__((aligned(4))) { \
+  DWORD FunctionLength:18; \
+  DWORD Version:2; \
+  DWORD ExceptionDataPresent:1; \
+  DWORD EpilogInHeader:1; \
+  DECLARE_FunctionFragment; \
+  DWORD EpilogCount:5; \
+  DWORD CodeWords:CODE_WORDS_BITS; \
+  struct { \
+    USHORT EpilogCount; \
+    BYTE CodeWords; \
+    BYTE Reserved; \
+  } Extended[!!(((epilog_count) == 0 || (epilog_count) >= 32) && 
((unwind_codes_size) == 0 || ((unwind_codes_size)+3)/4 >= 
12*CODE_WORDS_BITS))]; \
+  DWORD Epilog[epilog_count]; \
+  BYTE UnwindCode[unwind_codes_size]; \
+  /* if unwind_codes_size is not divisible by 4 then there is an automatic 
padding */ \
+  ULONG ExceptionHandler __attribute__((aligned(4))); \
+  ULONG ExceptionData[exception_data_count]; \
+}
+
+#ifdef __arm__
+#define UNWIND_CODE_TABLE { \
+  0xCB,                 /* mov sp, r11 */ \
+  0xA8, 0x00,           /* pop.w {r11, lr} */ \
+  0xFF,                 /* bx lr */ \
+}
+#else /* __aarch64__ */
+#define UNWIND_CODE_TABLE { \
+  UWOP_SET_FP,          /* mov sp, x29 */ \
+  UWOP_SAVE_FPLR_X(16), /* ldp x29, lr, [sp], #16 */ \
+  UWOP_END,             /* ret */ \
+}
+#endif
+
+#define UNWIND_CODE_TABLE_SIZE sizeof((BYTE[])UNWIND_CODE_TABLE)
+
 #else
 
 #error unsupported platform
@@ -119,6 +181,13 @@ static const UNWIND_INFO(UNWIND_CODE_TABLE_COUNT, 0) 
all_functions_unwind_xdata
   .FrameRegister = UWREG_RBP,
   .UnwindCode = UNWIND_CODE_TABLE,
 };
+#elif defined(__arm__) || defined(__aarch64__)
+static UNWIND_INFO(0, UNWIND_CODE_TABLE_SIZE, 0) all_functions_unwind_xdata = {
+  .FunctionLength = -1, /* filled at runtime */
+  .Version = 0,
+  .CodeWords = (UNWIND_CODE_TABLE_SIZE+3)/4,
+  .UnwindCode = UNWIND_CODE_TABLE,
+};
 #else
 #error unsupported platform
 #endif
@@ -142,6 +211,9 @@ static inline __attribute__((always_inline)) int 
register_runtime_image_unwind_i
   /* All addresses in UNWIND_INFO and RUNTIME_FUNCTION are relative the image 
base. */
   extern IMAGE_DOS_HEADER __ImageBase;
   const DWORD image_size = ((IMAGE_NT_HEADERS*)((BYTE*)&__ImageBase + 
__ImageBase.e_lfanew))->OptionalHeader.SizeOfImage;
+#if defined(__arm__) || defined(__aarch64__)
+  all_functions_unwind_xdata.FunctionLength = image_size / 
(sizeof(uintptr_t)/2);
+#endif
 #if defined(__x86_64__)
   all_functions_pdata.EndAddress = image_size;
 #endif
@@ -168,6 +240,15 @@ static UNWIND_INFO(UNWIND_CODE_TABLE_COUNT, 0) 
function_unwind_xdata = {
   .UnwindCode = UNWIND_CODE_TABLE,
   .ExceptionHandler = -1, /* filled at runtime */
 };
+#elif defined(__arm__) || defined(__aarch64__)
+static UNWIND_INFO(0, UNWIND_CODE_TABLE_SIZE, 0) function_unwind_xdata = {
+  .FunctionLength = -1, /* filled at runtime */
+  .Version = 0,
+  .ExceptionDataPresent = 1,
+  .CodeWords = (UNWIND_CODE_TABLE_SIZE+3)/4,
+  .UnwindCode = UNWIND_CODE_TABLE,
+  .ExceptionHandler = -1, /* filled at runtime */
+};
 #else
 #error unsupported platform
 #endif
@@ -192,6 +273,9 @@ static inline __attribute__((always_inline)) int 
register_runtime_function_excep
 {
   /* All addresses in UNWIND_INFO and RUNTIME_FUNCTION are relative the image 
base. */
   extern IMAGE_DOS_HEADER __ImageBase;
+#if defined(__arm__) || defined(__aarch64__)
+  function_unwind_xdata.FunctionLength = (function_end - function_begin) / 
(sizeof(uintptr_t)/2);
+#endif
   function_unwind_xdata.ExceptionHandler = (uintptr_t)exception_handler - 
(uintptr_t)&__ImageBase;
   function_pdata.BeginAddress = function_begin - (uintptr_t)&__ImageBase;
 #if defined(__x86_64__)
-- 
2.20.1

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to