On Tue, 2 Apr 2019, Jacek Caban wrote:
We currently have pretty useless chunk of assembly in mingwex that recently
caused problems with Wine mingw builds. The problem is that we have longjmp
symbol defined in mingwex which in fact forwards the call to the imported
function on x64 non-SEH and x86. I did some digging in git history, but it
doesn't explain much about why it's needed.
The one thing I'm not sure is non-SEH x64 build. In this case, it sets frame
stored in jmp_buf to 0 before calling the actual implementation. There is
absolutely no explanation for it. Maybe it was needed at some point for sjlj
exception handling? Even if that's the case, it would be a pretty broken
design. If such nulling is needed, it should be done before calling longjmp;
intercepting an existing function is definitely not the right way to do it.
Maybe it's not really needed?
And yet another question is, is non-SEH x64 build still something we need to
care about?
If we still care about non-SEH x64 builds and really need to carry this hack,
that could be preserved at the cost of complexity. But my guess is that we
don't need it and that's what this patch does.
Regarding the patch itself, it looks like you're losing the longjmp
function altogether from msvcrt.def.in, instead of making it available for
all arches?
I believe the reason for clearing the frame field of jmp_buf, is that the
msvcrt setjmp/longjmp actually does unwinding with SEH, instead of just
restoring the stored context from jmp_buf. The exact details of this seems
to differ a bit between architectures (and potentially between msvcrt
versions).
When doing the setjmp, setjmp is expanded into _setjmp (or a variation
thereof), where the second parameter is a pointer to the current stack
frame. When doing longjmp, it does a SEH unwind of the stack until it
finds a frame that matches this pointer. This parameter afaik is stored in
the frame field of jmp_buf.
What actually happens if SEH information is unavailable varies quite a
bit. When testing on ARM and ARM64, the longjmp will crash if SEH is
unavailable, when _setjmpex is called with a non-null frame pointer. On
ARM, if the second parameter to _setjmpex is NULL though, the longjmp does
succeed, but on ARM64 this doesn't help, SEH information simply is
mandatory. (And for ARM64, the exact form of the frame pointer needed is a
bit more tricky; llvm had to add a new intrinsic function for getting it.)
Given this, it seems like the clearing of the frame field is to make sure
that the longjmp works as a plain longjmp without SEH unwinding (like it
worked for me on ARM, unlike ARM64). However, I just tested building a
toolchain with SJLJ, i.e. without SEH, for x86_64, and setjmp/longjmp does
seem to work (on Windows 10 18.09, with ucrt) even if the frame pointer is
left non-null. In this case, the executable contained no SEH
information altogether. But in a more mixed build, where the executable
does contain some SEH information (but incomplete, unable to do the unwind
using it), things fail unless the field is cleared.
However, instead of intercepting the longjmp function, it's probably
better to just pass NULL for this parameter to begin with, if SEH isn't
available. With the attached patch, this seems to work fine for me.
So in general, I'd still like to keep x86_64 builds without SEH usable.
(For llvm-mingw, this was the only configuration of x86_64 up until last
summer.) But with the patch I just sent, your patch should be fine (modulo
the misedit in msvcrt.def.in).
// Martin
From 70244dca36207da95170d8f67e21696d0567390b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <[email protected]>
Date: Tue, 2 Apr 2019 23:13:59 +0300
Subject: [PATCH] headers: Only pass a frame pointer to setjmp if SEH is
enabled
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This should avoid the need for clearing the frame pointer in the
longjmp wrapper.
Signed-off-by: Martin Storsjö <[email protected]>
---
mingw-w64-headers/crt/setjmp.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/mingw-w64-headers/crt/setjmp.h b/mingw-w64-headers/crt/setjmp.h
index fc79f1091..547dfde2f 100644
--- a/mingw-w64-headers/crt/setjmp.h
+++ b/mingw-w64-headers/crt/setjmp.h
@@ -217,18 +217,20 @@ void * __cdecl __attribute__ ((__nothrow__)) mingw_getsp (void);
# define setjmp(BUF) __mingw_setjmp((BUF))
# define longjmp __mingw_longjmp
int __cdecl __attribute__ ((__nothrow__,__returns_twice__)) __mingw_setjmp(jmp_buf _Buf);
-# else
+# elif defined(__SEH__)
# if (__MINGW_GCC_VERSION < 40702)
# define setjmp(BUF) _setjmp((BUF), mingw_getsp())
# else
# define setjmp(BUF) _setjmp((BUF), __builtin_frame_address (0))
# endif
+# else
+# define setjmp(BUF) _setjmp((BUF), NULL)
# endif
int __cdecl __attribute__ ((__nothrow__,__returns_twice__)) _setjmp(jmp_buf _Buf, void *_Ctx);
int __cdecl __attribute__ ((__nothrow__,__returns_twice__)) _setjmp3(jmp_buf _Buf, void *_Ctx);
# else
# undef setjmp
-# ifdef _WIN64
+# ifdef __SEH__
# if (__MINGW_GCC_VERSION < 40702)
# define setjmp(BUF) _setjmpex((BUF), mingw_getsp())
# define setjmpex(BUF) _setjmpex((BUF), mingw_getsp())
--
2.17.1
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public