Clang doesn’t work very well with static libstdc++ build for MinGW resulting in “multiple definition …” or “undefined reference” to the symbols.
Let’s consider following code (removed includes and main for readability):
multiple definition:
std::string a = “a”;
std::string b = ‘@’ + a;
This simple code build with -static flag will cause this error:
D:\msys64\mingw64\lib\gcc\x86_64-w64-mingw32\6.3.0\libstdc++.a(string-inst.o):(.text$ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EES5_RKS8[ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EES5_RKS8]+0x0): multiple definition of `std::__cxx11::basic_string<char, std::char_traits, std::allocator > std::operator+<char, std::char_traits, std::allocator >(char, std::__cxx11::basic_string<char, std::char_traits, std::allocator > const&)’
D:\msys64\tmp\string-e39e30.o:(.text[ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EES5_RKS8]+0x0): first defined here
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
undefined reference
std::u16string b;
This time adding -static to clang arguments will cause this error (note: __imp___cxa_call_unexpected exists only in dynamic libstdc++):
D:\msys64\tmp\string-7062fd.o:(.text[_ZNSt7__cxx1112basic_stringIDsSt11char_traitsIDsESaIDsEED2Ev]+0x4a): undefined reference to __imp___cxa_call_unexpected' D:\msys64\tmp\string-7062fd.o:(.text[__clang_call_terminate]+0x7): undefined reference to __imp___cxa_begin_catch’
D:\msys64\tmp\string-7062fd.o:(.text[_ZNSt7__cxx1112basic_stringIDsSt11char_traitsIDsESaIDsEE10_M_destroyEy]+0x72): undefined reference to `__imp___cxa_call_unexpected’
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
It is probably related to the way the symbols are build into static libstdc++ for MinGW.
Let’s take a look at weak symbol from different test cases:
MinGW, dynamic libstdc++:
0000000000000000 I __imp__ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
0000000000000000 T _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
MinGW, static libstdc++:
0000000000000000 p .pdata$_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
0000000000000000 t .text$_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
0000000000000000 r .xdata$_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
0000000000000000 T _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
Linux, static libstdc++:
0000000000000000 W _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
This greatly reduces Clang usefulness on Windows with MinGW.
All responses are welcome.
This code assumes that all compiler-referenced runtime functions on COFF are dllimport, unless we happen to see a prototype for them during compilation that says otherwise. Saleem felt we shouldn’t rely on the import thunks that the linker generates when you reference an imported function but forget to declare it as dllimport during compilation. It has some performance benefits, but it never really bothered me. Most users don’t complain about the extra absolute branch imposed by the PLT on ELF, and those that do are getting -fno-plt soon.
Anyway, you can see the code difference easily here:
The other error looks like some kind of COMDAT disagreement between GCC and Clang that will require further investigation. It might relate to our choice to stop using “.text$function_name” for the section.
Building with `-flto-visibility-public-std` should allow the static build
to work. I think that having support for a static c++ library makes sense,
but need to expose it through a flag to have it behave like `/MT` vs `/MD`.
There is a small performance cost for the thunks, plus binary size benefits
for very large binaries (especially with the BFD linker). IIRC, some
versions of lldb also had trouble with the import thunks when generated by
the BFD linker. I would rather have this be the default and have an
opt-out mechanism rather than the inverse (a la `-fno-plt`).
This is what I’ve got for MSYS2 [0] and looks like there are no regressions but I couldn’t get make check to run (multiple definition errors in LLVM).
I haven’t tested this patch on Ubuntu but adding -flto-visibility-public-std to CC1 solves this issue [1] so I belive will help there.
Instead of reverting dllimport changes final solution could be similar to this patch but probably it would require writing some tests but I doubt I’ll have time to learn how to do it with clang soon.