lhmouse / mcfgthread

Cornerstone of the MOST efficient std::thread on Windows for mingw-w64
https://gcc-mcf.lhmouse.com/
Other
269 stars 28 forks source link

Using MCF thread model with SJLJ exception handling results in infinite recursion #91

Closed starg2 closed 10 months ago

starg2 commented 10 months ago

I managed to build GCC 13.2 with MCF thread model and SJLJ exception handling using mingw-builds. While it can generate working C programs, even a simple C++ hello world program compiled with it causes segmentation fault. The stack trace suggests infinite recursion between fc_key_init_once() and _Unwind_SjLj_Register():

#0  0x00007ff9a42566af in fc_key_init_once () at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:112
#1  0x00007ff9a4256b05 in _Unwind_SjLj_Register (fc=0x4040a0)
    at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:124
#2  0x00007ff9a42566eb in fc_key_init_once () at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:112
#3  0x00007ff9a4256b05 in _Unwind_SjLj_Register (fc=0x404180)
    at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:124
#4  0x00007ff9a42566eb in fc_key_init_once () at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:112
#5  0x00007ff9a4256b05 in _Unwind_SjLj_Register (fc=0x404260)
    at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:124

and finally:

#18540 0x00007ff9a42566eb in fc_key_init_once () at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:112
#18541 0x00007ff9a4256b05 in _Unwind_SjLj_Register (fc=0x5ff020)
    at ../../../../../src/gcc-13.2.0/libgcc/unwind-sjlj.c:124
#18542 0x00007ff94102122a in std::ios_base::Init::Init (this=<optimized out>)
    at ../../../../../../../src/gcc-13.2.0/libstdc++-v3/src/c++98/ios_init.cc:78
#18543 0x00007ff9410211cd in std::ios_base::Init::Init (this=this@entry=0x7ff9410566a0 <std::__ioinit>)
    at ../../../../../../../src/gcc-13.2.0/libstdc++-v3/src/c++98/ios_init.cc:121
#18544 0x00007ff94104e120 in __static_initialization_and_destruction_0 ()
    at ../../../../../../../src/gcc-13.2.0/libstdc++-v3/src/c++98/globals_io.cc:109
#18545 _GLOBAL__sub_I.00090__ZSt3cin(void) ()
    at ../../../../../../../src/gcc-13.2.0/libstdc++-v3/src/c++98/globals_io.cc:109
#18546 0x00007ff940ebafd2 in __do_global_ctors ()
   from D:\gcc\msys64\home\user\ucrt-mcf-sjlj-13-stage1\x86_64-1320-mcf-sjlj-ucrt-rt_v12\mingw64\bin\libstdc++-6.dll
#18547 0x00007ff940eb130a in __DllMainCRTStartup ()
   from D:\gcc\msys64\home\user\ucrt-mcf-sjlj-13-stage1\x86_64-1320-mcf-sjlj-ucrt-rt_v12\mingw64\bin\libstdc++-6.dll
#18548 0x00007ff9e343869f in ntdll!RtlActivateActivationContextUnsafeFast () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18549 0x00007ff9e347d04d in ntdll!RtlEnumerateEntryHashTable () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18550 0x00007ff9e347cdfe in ntdll!RtlEnumerateEntryHashTable () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18551 0x00007ff9e347ce70 in ntdll!RtlEnumerateEntryHashTable () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18552 0x00007ff9e34eea75 in ntdll!LdrInitShimEngineDynamic () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18553 0x00007ff9e34da184 in ntdll!EtwLogTraceEvent () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18554 0x00007ff9e3483eb3 in ntdll!LdrInitializeThunk () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18555 0x00007ff9e3483dde in ntdll!LdrInitializeThunk () from C:\WINDOWS\SYSTEM32\ntdll.dll
#18556 0x0000000000000000 in ?? ()
lhmouse commented 10 months ago

Looks like __gthread_once() attempts to initialize exception data: https://github.com/lhmouse/mcfgthread/blob/master/mcfgthread/gthr.h#L344-L359

As this is within initialization of the stack unwinder, the __gthread_once() call here can't use exceptions; we may have to assume that exceptions can't happen at all:

So a fix may look like

diff --git a/libgcc/unwind-sjlj.c b/libgcc/unwind-sjlj.c
index 01338850037..3e4a42dfddd 100644
--- a/libgcc/unwind-sjlj.c
+++ b/libgcc/unwind-sjlj.c
@@ -111,7 +111,13 @@ static void
 fc_key_init_once (void)
 {
   static __gthread_once_t once = __GTHREAD_ONCE_INIT;
-  if (__gthread_once (&once, fc_key_init) != 0 || use_fc_key < 0)
+  if (_MCF_once_wait (&once, nullptr) == 1)
+    {
+      fc_key_init ();
+      _MCF_once_release (&once);
+    }
+
+  if (use_fc_key < 0)
     use_fc_key = 0;
 }
 #endif
starg2 commented 10 months ago

That did the trick with s/nullptr/NULL/. Thanks!

lhmouse commented 10 months ago

I was thinking about whether __gthread_once() should restore the flag in case of an exception; otherwise, the flag will be left in an unusable state. The Glibc pthread_once() does not restore it, and causes potential deadlocks. Apparently their decision is that it will not get fixed: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66146#c54

Maybe mcfgthread could have followed that, making std::call_once() buggy and unusable.... and there wouldn't have been this GitHub Issue.

tomay3000 commented 10 months ago

I also want SJLJ with MCF, For me, it didn't even compile, here are my Github Actions: https://github.com/tomay3000/mingw-builds-binaries/actions/runs/6829799967. Could you please share your gcc patches with us so I can apply it directly to my mingw-builds allowing the gcc to build. TIA.

lhmouse commented 10 months ago

what was the error?

starg2 commented 10 months ago

@tomay3000 PR is ready at niXman/mingw-builds#663.

tomay3000 commented 10 months ago

@tomay3000 PR is ready at niXman/mingw-builds#663.

Thanks, Can't wait to try it out.

lhmouse commented 4 months ago

I would like to permanently solve this by https://github.com/lhmouse/mcfgthread/commit/b91a7a6e7d7fa1483b337b2dbebe1f97f984ce2f.

The downside is that at the moment neither GCC or Clang implements SEH, so exceptions that escape from once-initialization procedures will result in abnormal termination. I think people should have been aware of the limitation of DWARF exception handling, no?