-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exception handling is broken on mingw32 when using static runtime libraries. #9289
Comments
is this attempting to throw across a module boundary? That can be an issue, but doesn't appear to be the case here as far as I can see from the backtrace. |
hmm guess this is what is also hitting a gcc clang build if trying to link to static libgcc it will fail to build. |
As I said, I tried to create a minimal example where I also threw exceptions over library boundaries. But I could not reproduce the error there. Unfortunately I don't understand enough about exception handling models like dwarf to be able to isolate the issue better. |
Seems like this also affects #9088 |
guess we should report this upstream, thats a major breakage :O |
this also affected the tdm build i was maintaining, and it seems to go back further than i thought. |
Not sure if there is any correlation, but there was a similar problem in ccache with the MIPS toolchain when using the gold linker instead of the bfd linker: ccache/ccache#907 |
wow thats quite a problem :S |
I was running in the same or similar bug during the debugging of ccache built with 64 bit gcc. After that, I took some time to look at the bug more closely, and the behavior gets weirder as I looked on it. 32-bit with static linkingThe process died on the following assert, without any error message:
The reason is located in the
I tried using a memory breakpoint to see if these pointers are ever set, but couldn't see it. 64-bit static/dynamicI was searching for the reason for a The position of the exit seems to be the normal place when no catch block was found:
32-bit dynamicThen I thought I'll debug the dynamic 32-bit version, but the gcc-libs havn't any symbols. Now everything was broken! Every process crashed at startup and it was impossible to debug any process (even with the 64-bit multiarch gdb). 32-bit static/dynamic with new gccI thought that the reason for the strange behavior was in the local build of the gcc libs, so I triggered a github action to build a new gcc package.
After that I started a cmd, added the new environment as the only entry in the PATH variable and built the ccache project. With static linking it was the same behavior as befor but with dynamic linking it was terminating with the following error message: After several tries to debug the behavior, it seems to be impossible to reproduce it with an attached debugger. But for me it was also not possible to detect the reason for this behavior. I'm not sure what is in the current gcc package, but it is not reproducible locally or on github. |
It's now broken in the dynamic case too -> #9771 As you correctly predicted I guess |
I tried rebuilding ccache without |
Minimal reproducer: #include <stdio.h>
#include <zstd.h>
int main()
{
try
{
printf("About to throw\n");
#ifdef BREAK_EXCEPTIONS
printf("Calling zstd: %u\n", ZSTD_versionNumber());
#endif
throw 42;
printf("After throw (unreachable)\n");
}
catch (...)
{
printf("Caught\n");
return 1;
}
return 0;
}
I chose zstd pretty arbitrarily, it could be any DLL that's dynamically linked to libgcc. |
Interestingly, it works with either |
We have fixed some of unwinding issues, does this still reproduce? |
I still get the same result with gcc 11.3.0 |
Here is an upstream comment on this issue: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105507#c5 |
these built with my TDM based toolset, exceptions work again guess it all came down to the grep problem we had. |
@rvogg @revelator any solution on that? You can check this issue: #5763 |
it is back?!? ok thats weird :S, not sure it will be looked at by those here you are probably better of reporting the bug at the gcc main site. also dont use __declspec(dllimport) with mingw (mingw uses autoimport so is a bit different than msvc). |
@revelator Thanks for your fast response. I just saw this page: https://www.msys2.org/docs/cpp/#known-issues |
-static alone should do unless g++ ignores it, in which case the gcc folks definatly need to know. |
-static-libgcc -static-libstdc++ is just if you have a project allready dependent on a gazillion dll's and dont also want them to rely on the gcc/g++ runtime dll's, for simple projects which dont rely on other libraries besides the windows import libraries or where you have static versions of every library it depends on -static alone will do :). there is a catch though if for example you use gcc to call g++ then -static-libstdc++ is ignored as gcc does not understand this flag and does not pass it on to g++. |
and as for why it still works for me, i use a special build of gcc based on the TDM mingw-w64 compiler with some fixes i been working on over the years. what makes the TDM based gcc different is that it allows throwing exceptions without the libgcc dll. it's not perfect in that it croaks on the aforementioned problem when some package decides to use gcc to call g++ creating a package which still depends on both the libstdc++ dll and libgcc dll because libstdc++ itself is built with a dependency on the libgcc dll. but for the rest it works just fine. |
It never went away, it's apparently a known limitation of gcc's exception handling. I think because it's not using native SEH mechanisms, which it is on other architectures like x86_64. Why not use dllimport? It saves a thunk (call to an indirect jmp instruction) in some cases. And in cases of variables, it may be necessary. In any event, it is the correct thing to do if the symbol is imported from a dll. |
just an observation that it seems to break at times (also noted from several sources that the import section is defined as empty in case of mingw) i also had success building sources that would normally croak with mingw if i changed the abi imports to empty). |
@revelator I am not sure, if libstd.dll and libgcc.dll are covered by the Runtime Library exception. So that's why I link to shared libraries with -static-libgcc and -static-libstdc++ |
ah ok :) . yeah i dont remember if it is covered either tbh ?. |
https://mingw-w64-public.narkive.com/Zbonasdx/problems-with-dllimport-dllexport this was probably the reason back then but it might be fixed now. EDIT: it had problems when autoimport was defined i found the old bug report again which explained it. |
I don't think it's caused by SEH vs dwarf-2. Clang (with libunwind) also uses dwarf-2 for 32-bit but is not affected with the sames issues AFAIK. So that would be entirely libgcc implementation fault.
Yeah, dllimport is the way to go if build system handles it correctly.
The implementation of exception handling in 32-bit and 64-bit compilers is vastly different for all compilers on Windows. Even between 32-bit GCC and 32-bit Clang with both using dwarf-2 it's entirely different code. |
indeed :), one can think of the libgcc dll as the same as microsofts msvcrt dll (if windows actually allowed it you could copy it to c:/windows/syswow64 and it would work just like the msvcrt dll just with dwarf exceptions instead of the native seh based crt). but here comes a problem... the native windows system dll's are SEH based so throwing dwarf exceptions across system dll's will most likely lead to crashes (bit like a tourist using google translate to explain something to a native some of the meaning gets lost) so gcc and the mingw abi uses a lot of magic to allow it to work regardless (well in most cases). sadly we newer got SEH for the 32 bit compiler and sometimes it shows (magic failure), sjlj is slower but has better compatibility with SEH though it still fails at times to. the 64 bit gcc uses SEH so does not suffer from all of this in fact exceptions work just fine even if you opt to not use the gcc dll's at all sigh... |
No. libgcc dll is libgcc (unwinding and certain low-level helper functions called by gcc-generated code)... msvcrt is libc (much much bigger). These are different beasts entirely, even on Linux. |
The way I see it, the problem with statically linked libgcc is similar to the main problem with statically-linked msvcrt with msvc: each module then has its own (potentially different) copy of the library, with its own globals. In that case, there is no single source of truth for unwinding information. With SEH, the OS (probably ntdll RtlUnwind functions) is that single source of truth. With libgcc dll, the single dll instance is. Sure, it could probably use |
bits of the crt seem to be part of libgcc (low level stuff indeed) msvcrt is still needed for actuall abi stuff while the libgcc dll handles unwinding. not sure how clang handles those parts but since exceptions only ever really apply to c++ it might have a copy of the static unwinders in libc++ maybe ?. huh seems it does...
looks like all the non __imp functions are from the static unwinders ???. |
just checked the libstdc++ import library and they are indeed missing there but located in libgcc_s.a which is a dependency of the libstdc++ dll anyway. oh boy :S |
heh this is weird clangs libunwind dll holds the same exports but the libc++ dll does not depend at all on the libunwind dll while libstdc++ depends on the libgcc dll, i wonder if those symbols are explicitly set in clang so as to not depend directly on the libunwind dll ?. if so kinda a nice workaround :) and one that might benefit gcc as well. |
How is this dllimport related to this issue? |
not sure if it is just something to note as sometimes it broke with __declspec(dllimport) if auto import was enabled (it is by default). |
It's enabled here https://github.com/llvm/llvm-project/blob/df168427b314f057c739eaccb21f361d3628f03b/libcxxabi/CMakeLists.txt#L116-L121 with There is no magic here, static libunwind is linked into libc++ as a whole archive or similar way.
No, it would break unwinding across DLLs in 32-bit builds with GCC (if it ever gets fixed). |
oh so we could actually use the libunwind dll if we disabled that flag or does it break anything ?. |
The last time I tested everything was ok, but with the static library we can spare the trouble of distributing another DLL with negligible binary size increase. |
If you build ccache >= 4.2 for mingw32, it crashes at runtime.
This bug started to occur when static linking was enabled for the gcc runtime libraries. (ccache/ccache#732)
The error almost always occurs in the same place:
In Util.cpp read_file rais an exception:
But the catch block in the calling function is never reached.
Here is the callstack of "ccache -s" :
I can't generate a minimal example for this bug. But, since the prebuilt 32 bit versions of ccache do not have this bug I assume that it is caused by the mingw32 environment and not by ccache.
The text was updated successfully, but these errors were encountered: