MINGW-packages: Issues when building with clang (duplicate libstdc++.a symbols)

Hello,

I am currently testing clang compilation and ran into several issues. Due to #1669 being unfixed at this moment I mostly relied on 3.8.0 win32 and 3.9.0 rc3 win32, however I tested mingw-w64-i686-clang (3.8) as well. Due to reports getting large I decided to split them.

2. A number of symbols present in libstdc++ headers are exported by libstdc++.a The issue is pretty straight-forward. When performing optimised static linking to libstdc++.a one is likely to face multiple symbol definition issue.

Minimal sample (issue confirmed on any clang): clang++ -target i686-w64-mingw32 -static -std=c++11 -pthread -O1 example2.cpp

#include <string>

int main () {
    auto s = std::string("1234");
    auto ws = std::u16string(u"1234");
    auto s1 = "#" + s;
}

This will produce the following error message:

C:\msys64\mingw32\lib\gcc\i686-w64-mingw32\6.1.0\libstdc++.a(string-inst.o):(.text$_ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EEPKS5_RKS8_[__ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EEPKS5_RKS8_]+0x0): multiple definition of `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
C:\msys64\tmp\example2-b21e71.o:(.text[__ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EEPKS5_RKS8_]+0x0): first defined here
C:\msys64\mingw32\lib\gcc\i686-w64-mingw32\6.1.0\libstdc++.a(string-inst.o):(.text$_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag[__ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag]+0x0): multiple definition of `void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag)'
C:\msys64\tmp\example2-b21e71.o:(.text[__ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag]+0x0): first defined here
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)

As it can be seen from the error the bug is caused by exported symbols in libstdc++.a. I do not expect it to export any _M_construct but it does this for char-instantiated one. I.e. u16string has no this bug. One is also required to enable optimisation (e.g. -O1) to get compilation failure.

I could imagine various ways to get this fixed, for example, SFINAE. I used a force_inline attribute.

Suggested changes: msys64\mingw32\include\c++\6.1.0\bits\basic_string.h

      // For forward_iterators up to random_access_iterators, used for
      // string::iterator, _CharT*, etc.
      template<typename _FwdIterator>
#ifdef __clang__
        __attribute__((always_inline))
#endif
        void
        _M_construct(_FwdIterator __beg, _FwdIterator __end,
             std::forward_iterator_tag);

and

  /**
   *  @brief  Concatenate C string and string.
   *  @param __lhs  First string.
   *  @param __rhs  Last string.
   *  @return  New string with value of @a __lhs followed by @a __rhs.
   */
  template<typename _CharT, typename _Traits, typename _Alloc>
#ifdef __clang__
    __attribute__((always_inline))
#endif
    basic_string<_CharT,_Traits,_Alloc>
    operator+(const _CharT* __lhs,
          const basic_string<_CharT,_Traits,_Alloc>& __rhs);

These methods may not be the only ones but are so far the only noticed ones by myself.

Also, I had to pass -pthread to get the sample compile without undefined pthread symbols. This feels undesired but I do not know any details about who is responsible here.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 4
  • Comments: 15

Commits related to this issue

Most upvoted comments

Here is a really small example of the inline function ABI incompatibility:

$ cat a.cpp
inline int foo(int a, int b) { return a + b; }
int bar(void);
int main() { return foo(1, 2) + bar(); }

$ cat b.cpp
inline int foo(int a, int b) { return a + b; }
int bar() { return foo(3, 4); }

$ gcc a.cpp -c -o a.o && clang --target=x86_64-w64-windows-gnu -c b.cpp -o b.o 

$ gcc a.o b.o -o a.exe
b.o:(.text[_Z3fooii]+0x0): multiple definition of `foo(int, int)'
a.o:a.cpp:(.text$_Z3fooii[_Z3fooii]+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

Going back to the section name .text$_Z3fooii in clang makes the problem go away.

A bit more: msys64\mingw32\include\c++\6.1.0\bits\basic_string.h:

      /**
       *  @brief  Construct string as copy of a range.
       *  @param  __beg  Start of range.
       *  @param  __end  End of range.
       *  @param  __a  Allocator to use (default is default allocator).
       */
#if __cplusplus >= 201103L
      template<typename _InputIterator,
           typename = std::_RequireInputIter<_InputIterator>>
#else
      template<typename _InputIterator>
#endif
#ifdef __clang__
        __attribute__((always_inline))
#endif
        basic_string(_InputIterator __beg, _InputIterator __end,
             const _Alloc& __a = _Alloc())
    : _M_dataplus(_M_local_data(), __a)
    { _M_construct(__beg, __end); }

msys64\mingw32\include\c++\6.1.0\bits\shared_ptr_base.h:

      void
#ifdef __clang__
      __attribute__((always_inline))
#endif
      _M_release() noexcept
      {
        // Be race-detector-friendly.  For more info see bits/c++config.

and

      // Called when _M_weak_count drops to zero.
      virtual void
      _M_destroy() noexcept
#ifdef __clang__
      ;
#else
      { delete this; }
#endif

msys64\mingw32\include\c++\6.1.0\typeinfo:

#ifdef __clang__
      __attribute__((always_inline))
#endif
    bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT

This looks like it was probably caused by my change in r268331, which makes us emit functions into subsections called “.text” instead of subsections called “.text$_Z3foov” where _Z3foov is the symbol name. I think I didn’t realize how incompatible the COFF port of ld.bfd is with the Visual C++ linker. I kind of assumed all that low-level object file stuff would be the same, since it’s the most well-documented thing we have to work with. Now I’m really sad. 😦