On 23.04.2025 18:57, Martin Storsjö wrote:
On Wed, 23 Apr 2025, Jacek Caban wrote:

Based on Wine code by Alexandre Julliard.
---
v2:
- extended a comment about unwinding entry thunk in setjmp
- __imp_ symbol more consistent with the codebase
- added a local RtlUnwind declaration with __MINGW_ATTRIB_NORETURN to avoid -Winvalid-noreturn warning

Hmm - we did add __MINGW_ATTRIB_NORETURN on RtlRestoreContext - should we add it on RtlUnwind and RtlUnwindEx as well?


That’s not how MSVC defines it, but I suppose we can go with it, especially since we already do the same for RtlRestoreContext.



mingw-w64-crt/Makefile.am            |   8 +-
mingw-w64-crt/misc/arm64ec/longjmp.c |  32 ++++++++
mingw-w64-crt/misc/arm64ec/setjmp.c  | 106 +++++++++++++++++++++++++++
mingw-w64-crt/misc/longjmp.S         |   2 +
mingw-w64-crt/misc/setjmp.S          |   2 +
5 files changed, 149 insertions(+), 1 deletion(-)
create mode 100644 mingw-w64-crt/misc/arm64ec/longjmp.c
create mode 100644 mingw-w64-crt/misc/arm64ec/setjmp.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index d16ebdcf2..04b61c429 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -461,6 +461,12 @@ src_ucrtapp=\
  string/wcsrchr.c \
  string/wcsstr.c

+if ARM64EC
+src_ucrtapp_arm64=\
+  misc/arm64ec/longjmp.c \
+  misc/arm64ec/setjmp.c
+endif
+
# Files included in libucrt*.a on x86_32
src_ucrtbase32=\
  $(src_ucrtbase) \
@@ -2563,7 +2569,7 @@ libarm64_libmsvcrt_extra_a_CPPFLAGS=$(CPPFLAGSARM64) -D__LIBMSVCRT__ -D__LIBMSVC
libarm64_libmsvcrt_extra_a_LIBADD=$(src_msvcrtarm64_x64)
libarm64_libucrt_extra_a_SOURCES = $(src_ucrtbasearm64)
libarm64_libucrt_extra_a_CPPFLAGS=$(CPPFLAGSARM64) -D__LIBMSVCRT__ $(extra_include) $(sysincludes)
-libarm64_libucrtapp_extra_a_SOURCES = $(src_ucrtapp)
+libarm64_libucrtapp_extra_a_SOURCES = $(src_ucrtapp) $(src_ucrtapp_arm64) libarm64_libucrtapp_extra_a_CPPFLAGS=$(CPPFLAGSARM64) -D__LIBMSVCRT__ $(extra_include) $(sysincludes)

libarm64_DATA += libarm64/libvcruntime140.a
diff --git a/mingw-w64-crt/misc/arm64ec/longjmp.c b/mingw-w64-crt/misc/arm64ec/longjmp.c
new file mode 100644
index 000000000..937394c93
--- /dev/null
+++ b/mingw-w64-crt/misc/arm64ec/longjmp.c
@@ -0,0 +1,32 @@
+/**
+ * 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.
+ */
+
+#undef __MSVCRT_VERSION__
+#define _UCRT
+
+#include <setjmp.h>
+#include <windef.h>
+#include <winbase.h>
+
+NTSYSAPI __MINGW_ATTRIB_NORETURN void NTAPI RtlUnwind(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
+
+void __cdecl longjmp( jmp_buf b, int retval )
+{
+    _JUMP_BUFFER *buf = (_JUMP_BUFFER *)b;
+    EXCEPTION_RECORD rec;
+
+    if (!retval) retval = 1;
+
+    rec.ExceptionCode = STATUS_LONGJUMP;
+    rec.ExceptionFlags = 0;
+    rec.ExceptionRecord = NULL;
+    rec.ExceptionAddress = NULL;
+    rec.NumberParameters = 1;
+    rec.ExceptionInformation[0] = (DWORD_PTR)buf;
+    RtlUnwind( (void *)buf->Frame, (void *)buf->Rip, &rec, IntToPtr(retval) );
+}
+
+void (__cdecl *__MINGW_IMP_SYMBOL(longjmp))( jmp_buf b, int retval ) = longjmp; diff --git a/mingw-w64-crt/misc/arm64ec/setjmp.c b/mingw-w64-crt/misc/arm64ec/setjmp.c
new file mode 100644
index 000000000..00dc33fbf
--- /dev/null
+++ b/mingw-w64-crt/misc/arm64ec/setjmp.c
@@ -0,0 +1,106 @@
+/**
+ * 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.
+ */
+
+#undef __MSVCRT_VERSION__
+#define _UCRT
+
+#include <setjmp.h>
+#include <windef.h>
+#include <winbase.h>
+
+static inline UINT fpcsr_to_mxcsr( UINT fpcr, UINT fpsr )
+{
+    UINT ret = 0;
+
+    if (fpsr & 0x0001) ret |= 0x0001;      /* invalid operation */
+    if (fpsr & 0x0002) ret |= 0x0004;      /* zero-divide */
+    if (fpsr & 0x0004) ret |= 0x0008;      /* overflow */
+    if (fpsr & 0x0008) ret |= 0x0010;      /* underflow */
+    if (fpsr & 0x0010) ret |= 0x0020;      /* precision */
+    if (fpsr & 0x0080) ret |= 0x0002;      /* denormal */
+
+    if (fpcr & 0x0080000)    ret |= 0x0040;   /* denormals are zero */
+    if (!(fpcr & 0x0000100)) ret |= 0x0080;   /* invalid operation mask */
+    if (!(fpcr & 0x0000200)) ret |= 0x0200;   /* zero-divide mask */
+    if (!(fpcr & 0x0000400)) ret |= 0x0400;   /* overflow mask */
+    if (!(fpcr & 0x0000800)) ret |= 0x0800;   /* underflow mask */
+    if (!(fpcr & 0x0001000)) ret |= 0x1000;   /* precision mask */
+    if (!(fpcr & 0x0008000)) ret |= 0x0100;   /* denormal mask */
+    if (fpcr & 0x0400000)    ret |= 0x4000;   /* round up */
+    if (fpcr & 0x0800000)    ret |= 0x2000;   /* round down */
+    if (fpcr & 0x1000000)    ret |= 0x8000;   /* flush to zero */
+    return ret;
+}
+
+/* unwind context by one call frame */
+static void unwind_one_frame( CONTEXT *context )
+{
+    void *data;
+    ULONG_PTR base, frame, pc = context->Rip - 4;
+    RUNTIME_FUNCTION *func = RtlLookupFunctionEntry( pc, &base, NULL );
+
+    RtlVirtualUnwind( UNW_FLAG_NHANDLER, base, pc, func, context, &data, &frame, NULL );
+}
+
+/* fixup jump buffer information; helper for _setjmpex */
+static int __attribute__((used)) do_setjmpex( _JUMP_BUFFER *buf, UINT fpcr, UINT fpsr )
+{
+    CONTEXT context = { .ContextFlags = CONTEXT_FULL };
+
+    buf->MxCsr = fpcsr_to_mxcsr( fpcr, fpsr );
+    buf->FpCsr = 0x27f;
+
+    /* If the caller is x64, the buffer contains an entry thunk capture. +     * Attempt to unwind it to retrieve the actual x64 context, if applicable. */
+    context.Rbx = buf->Rbx;
+    context.Rsp = buf->Rsp;
+    context.Rbp = buf->Rbp;
+    context.Rsi = buf->Rsi;
+    context.Rdi = buf->Rdi;
+    context.R12 = buf->R12;
+    context.R13 = buf->R13;
+    context.R14 = buf->R14;
+    context.R15 = buf->R15;
+    context.Rip = buf->Rip;
+    memcpy( &context.Xmm6, &buf->Xmm6, 10 * sizeof(context.Xmm6) );
+    unwind_one_frame( &context );
+    if (!RtlIsEcCode( context.Rip ))  /* caller is x64, use its context instead of the ARM one */
+    {

One detail that I'm a little unsure of here; we unwind the potential thunk context. But what if that wasn't a thunk, but was a regular arm64 function. What if the caller of that was x86_64, and we end up in that after the one step unwind? Or I guess we would hit another thunk in that case - would that be arm64ec code?


Yes, that function would have its own entry thunk, and those are always ARM64EC (setting aside any unusual manually written thunks that perform a direct call, which isn't how it's supposed to be used).


Thanks,

Jacek



_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to