dnl @synopsis AX_DLL_STRING_FIX() dnl dnl Tests for, and creates a workaround for, a bug currently in g++ 3.4.4 on dnl Cygwin (maybe other targets with statically linked libstdc++?) where dnl passing an empty std::string to a dll will cause a crash on destruction dnl due to incorrect memory handling. See bug 24196 in gcc's bugzilla for dnl more details: dnl http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 dnl dnl @category XXX dnl @author Steven Brown dnl @version 2006-04-08 dnl @license GPLWithACException dnl Plan: compile conftest-dll.cc, conftest-exe.cc. It has a 'crash' param dnl that can be passed. Verify the compile worked and we can run it normally dnl ok. Then, pass 'crash', test if it crashed. If so, locate dnl basic_string.(h|tcc), copy them into the build dir's dnl include/ax_dll_string_fix/bits dir, apply patch from gcc's bugzilla, test dnl if the patch worked, add patch to force it to be explicitly instantiated to dnl avoid the buggy one compiled into libstdc++, add the path to includes, dnl rebuild the conftest stuff, check if crash now works. AC_DEFUN([AX_DLL_STRING_FIX], [ AC_MSG_CHECKING(if the dll string test can be compiled) cat << EOF > conftest-dll.cc [ #include using namespace std; void testit(std::string (*contentGet)()) { contentGet(); } ] EOF cat << EOF > conftest-exe.cc [ #include #include using namespace std; extern void testit(std::string (*contentGet)()); std::string contentGet() { return string(""); // Anything other than empty string works. } int main(int argc, char *argv[]) { if(argc == 2 && strcmp(argv[1], "crash") == 0) { testit(&contentGet); } return 0; } ] EOF $CXX -shared $CFLAGS $CPPFLAGS conftest-dll.cc -o conftest-dll.dll >&AS_MESSAGE_LOG_FD && $CXX $CFLAGS $CPPFLAGS conftest-exe.cc conftest-dll.dll -o conftest-exe.exe >& AS_MESSAGE_LOG_FD if test x"$?" = x"0"; then AC_MSG_RESULT(yes) dnl Make sure it runs normally first. AC_MSG_CHECKING(if the dll string test is usable) if /bin/sh -c "(LD_LIBRARY_PATH=\"$PWD\" ./conftest-exe.exe)" >& AS_MESSAGE_LOG_FD 2>&1; then AC_MSG_RESULT(yes) dnl Now we can check for the bug. AC_MSG_CHECKING(if the dll string test is affected by gcc bug 24196) if ! /bin/sh -c "(LD_LIBRARY_PATH=\"$PWD\" ./conftest-exe.exe crash)" >& AS_MESSAGE_LOG_FD 2>&1; then AC_MSG_RESULT(yes) dnl We have the bug, so we need to copy the basic_string files and dnl patch them. dnl Locate the basic_string files in use. BASIC_STRING_FILES=`cpp conftest-dll.cc | sed -n "s/^# [[:digit:]]\+ \"\(.*bits\/basic_string.\(h\|tcc\)\)\".*/\1/p" | sort | uniq` dnl Copy them to our special include dir and patch. mkdir -p ax_dll_string_fix/bits echo "$BASIC_STRING_FILES" | while read a; do cp "$a" ax_dll_string_fix/bits done cat << EOF > conftest-draft1.patch [ diff -ur libstdc++-v3-orig/include/bits/basic_string.h libstdc++-v3/include/bits/basic_string.h --- libstdc++-v3-orig/include/bits/basic_string.h 2005-04-25 13:02:01.000000000 +0200 +++ libstdc++-v3/include/bits/basic_string.h 2005-10-06 15:19:17.000000000 +0200 @@ -211,11 +211,8 @@ void _M_dispose(const _Alloc& __a) { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING - if (__builtin_expect(this != &_S_empty_rep(), false)) -#endif - if (__gnu_cxx::__exchange_and_add(&this->_M_refcount, -1) <= 0) - _M_destroy(__a); + if (__gnu_cxx::__exchange_and_add(&this->_M_refcount, -1) <= 0) + _M_destroy(__a); } // XXX MT void @@ -224,10 +221,7 @@ _CharT* _M_refcopy() throw() { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING - if (__builtin_expect(this != &_S_empty_rep(), false)) -#endif - __gnu_cxx::__atomic_add(&this->_M_refcount, 1); + __gnu_cxx::__atomic_add(&this->_M_refcount, 1); return _M_refdata(); } // XXX MT @@ -1984,11 +1978,7 @@ template inline basic_string<_CharT, _Traits, _Alloc>:: basic_string() -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING - : _M_dataplus(_S_empty_rep()._M_refdata(), _Alloc()) { } -#else - : _M_dataplus(_S_construct(size_type(), _CharT(), _Alloc()), _Alloc()) { } -#endif + : _M_dataplus(_S_empty_rep()._M_refcopy(), _Alloc()) { } // operator+ /** diff -ur libstdc++-v3-orig/include/bits/basic_string.tcc libstdc++-v3/include/bits/basic_string.tcc --- libstdc++-v3-orig/include/bits/basic_string.tcc 2004-10-28 23:52:40.000000000 +0200 +++ libstdc++-v3/include/bits/basic_string.tcc 2005-10-06 15:20:38.000000000 +0200 @@ -88,10 +88,9 @@ _S_construct(_InIterator __beg, _InIterator __end, const _Alloc& __a, input_iterator_tag) { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING if (__beg == __end && __a == _Alloc()) - return _S_empty_rep()._M_refdata(); -#endif + return _S_empty_rep()._M_refcopy(); + // Avoid reallocation for common case. _CharT __buf[128]; size_type __len = 0; @@ -136,10 +135,9 @@ _S_construct(_InIterator __beg, _InIterator __end, const _Alloc& __a, forward_iterator_tag) { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING if (__beg == __end && __a == _Alloc()) - return _S_empty_rep()._M_refdata(); -#endif + return _S_empty_rep()._M_refcopy(); + // NB: Not required, but considered best practice. if (__builtin_expect(__is_null_pointer(__beg) && __beg != __end, 0)) __throw_logic_error(__N("basic_string::_S_construct NULL not valid")); @@ -165,10 +163,9 @@ basic_string<_CharT, _Traits, _Alloc>:: _S_construct(size_type __n, _CharT __c, const _Alloc& __a) { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING if (__n == 0 && __a == _Alloc()) - return _S_empty_rep()._M_refdata(); -#endif + return _S_empty_rep()._M_refcopy(); + // Check for out_of_range and length_error exceptions. _Rep* __r = _Rep::_S_create(__n, size_type(0), __a); if (__n) @@ -362,10 +359,6 @@ basic_string<_CharT, _Traits, _Alloc>::_Rep:: _M_destroy(const _Alloc& __a) throw () { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING - if (this == &_S_empty_rep()) - return; -#endif const size_type __size = sizeof(_Rep_base) + (this->_M_capacity + 1) * sizeof(_CharT); _Raw_bytes_alloc(__a).deallocate(reinterpret_cast(this), __size); @@ -375,10 +368,6 @@ void basic_string<_CharT, _Traits, _Alloc>::_M_leak_hard() { -#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING - if (_M_rep() == &_S_empty_rep()) - return; -#endif if (_M_rep()->_M_is_shared()) _M_mutate(0, 0, 0); _M_rep()->_M_set_leaked(); ] EOF cat << EOF > conftest-force_implicit_instantiation.patch Force basic_string to be implicitly instantiated since we want to prevent the unpactched version compiled into libstdc++ from being used. - Steven Brown --- bits/basic_string.tcc-original 2006-04-09 18:04:49.390625000 -0700 +++ bits/basic_string.tcc 2006-04-09 18:04:54.281250000 -0700 @@ -928,7 +928,7 @@ // Inhibit implicit instantiations for required instantiations, // which are defined via explicit instantiations elsewhere. // NB: This syntax is a GNU extension. -#if _GLIBCXX_EXTERN_TEMPLATE +#if 0 extern template class basic_string; extern template basic_istream& EOF AC_MSG_CHECKING(if the dll string patch from bug 24196 can be applied) (cd ax_dll_string_fix && patch -p2 < ../conftest-draft1.patch >& AS_MESSAGE_LOG_FD && patch -p0 < ../conftest-force_implicit_instantiation.patch >& AS_MESSAGE_LOG_FD) if test x"$?" = x"0"; then AC_MSG_RESULT(yes) dnl Test to see if that fixed the bug. AC_MSG_CHECKING(if the dll string patch fixed the bug) $CXX -shared $CFLAGS -Iax_dll_string_fix $CPPFLAGS conftest-dll.cc -o conftest-dll.dll >&AS_MESSAGE_LOG_FD && $CXX $CFLAGS -Iax_dll_string_fix $CPPFLAGS conftest-exe.cc conftest-dll.dll -o conftest-exe.exe >& AS_MESSAGE_LOG_FD if test x"$?" = x"0"; then dnl Hopefully, it doesn't crash, now. if /bin/sh -c "(LD_LIBRARY_PATH=\"$PWD\" ./conftest-exe.exe crash)" >& AS_MESSAGE_LOG_FD 2>&1; then AC_MSG_RESULT(yes) dnl We're good to go - ensure that the patched files are used dnl instead of the originals. CPPFLAGS="-I$PWD/ax_dll_string_fix $CPPFLAGS" else AC_MSG_RESULT(no) AC_MSG_ERROR([*** The dll string bug appears to exist on this platform but the patch from gcc bug 24196 did not fix the bug. You should check that bug report and see if there is a newer patch available. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196]) fi else AC_MSG_RESULT(no) AC_MSG_ERROR([*** The dll string bug appears to exist on this platform but the patch from gcc bug 24196 caused compile errors. You should check that bug report and see if there is a newer patch available. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196]) fi else AC_MSG_RESULT(no) AC_MSG_ERROR([*** The dll string bug appears to exist on this platform but the patch from gcc bug 24196 could not be applied. You should check that bug report and see if there is a newer patch available. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196]) fi else AC_MSG_RESULT(no) fi else AC_MSG_RESULT(no) fi else AC_MSG_RESULT(no) fi ])