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?
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?
Other than this bit, this looks good to me now, thanks! // Martin _______________________________________________ Mingw-w64-public mailing list Mingw-w64-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mingw-w64-public