LTO 导致标准库崩溃
LTO causes crash in standard library
考虑以下程序:
#include <iostream>
#include <string>
int main()
{
std::string s;
std::getline(std::cin, s);
return 0;
}
我尝试使用各种标志和 运行 作为 echo foo | ./prog
来构建它。
如果我使用 clang 5.0 或 gcc 7.1(或 7.2)构建它并从 -O0
优化到 -O3
,它会按预期工作。但是,如果我将 -flto
添加到这些配置中的任何一个,它会立即崩溃并显示以下回溯:
/lib64/libc.so.6(+0x721af)[0x7f596b08e1af]
/lib64/libc.so.6(+0x77706)[0x7f596b093706]
/lib64/libc.so.6(+0x78453)[0x7f596b094453]
/usr/lib64/libstdc++.so.6(_ZNSs7reserveEm+0x85)[0x7f596b9ac055]
/usr/lib64/libstdc++.so.6(_ZSt7getlineIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_ES4_+0x175)[0x7f596b984c05]
./a.out[0x400d7d]
./a.out[0x400c32]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f596b03c6e5]
./a.out[0x400ab9]
Valgrind 以更易读的方式报告相同内容:
==30863== Invalid free() / delete / delete[] / realloc()
==30863== at 0x4C2A8DC: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==30863== by 0x4F0E054: std::string::reserve(unsigned long) (in /usr/lib64/libstdc++.so.6.0.24)
==30863== by 0x4EE6C04: std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char) (in /usr/lib64/libstdc++.so.6.0.24)
==30863== by 0x40091B: main (in /path/to/prog)
==30863== Address 0x6011c0 is 0 bytes inside data symbol "_ZNSs4_Rep20_S_empty_rep_storageE"
它在 --std=c++14
及以下甚至启用 LTO 时也能正常工作。
那么问题是什么?两个编译器中 C++17 的 LTO 实现是否存在错误?或者只是 libstdc++
是用错误的标志编译的?我使用 opensuse 42.3,标准库是从存储库安装的。
它能以某种方式解决吗?
对于 gcc,这看起来像这个错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82172。
有许多解决方法,请参阅 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82172#c3。其中之一是使用 -D_GLIBCXX_USE_CXX11_ABI=1
:
g++ -D_GLIBCXX_USE_CXX11_ABI=1 --std=c++17 -flto prog.cpp
这里也有同样的问题。
按照 ks1322 的链接,我终于找到了 binutils 错误 page,它
描述了问题的根源。
该错误最终在 binutils 2.30 中得到修复,并反向移植到 binutils 2.29 和 2.28。更新到 binutils 2.29 修复了测试示例,但我的实际构建仍然受到影响。
我发现我的项目依赖项之一(我使用 CMake 和 external project module)是在没有 LTO 的情况下构建的。将 -flto
添加到所有依赖项并使用适当的归档程序(即 gcc-ar
而不是 ar
)完全解决了问题。
考虑以下程序:
#include <iostream>
#include <string>
int main()
{
std::string s;
std::getline(std::cin, s);
return 0;
}
我尝试使用各种标志和 运行 作为 echo foo | ./prog
来构建它。
如果我使用 clang 5.0 或 gcc 7.1(或 7.2)构建它并从 -O0
优化到 -O3
,它会按预期工作。但是,如果我将 -flto
添加到这些配置中的任何一个,它会立即崩溃并显示以下回溯:
/lib64/libc.so.6(+0x721af)[0x7f596b08e1af]
/lib64/libc.so.6(+0x77706)[0x7f596b093706]
/lib64/libc.so.6(+0x78453)[0x7f596b094453]
/usr/lib64/libstdc++.so.6(_ZNSs7reserveEm+0x85)[0x7f596b9ac055]
/usr/lib64/libstdc++.so.6(_ZSt7getlineIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_ES4_+0x175)[0x7f596b984c05]
./a.out[0x400d7d]
./a.out[0x400c32]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f596b03c6e5]
./a.out[0x400ab9]
Valgrind 以更易读的方式报告相同内容:
==30863== Invalid free() / delete / delete[] / realloc()
==30863== at 0x4C2A8DC: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==30863== by 0x4F0E054: std::string::reserve(unsigned long) (in /usr/lib64/libstdc++.so.6.0.24)
==30863== by 0x4EE6C04: std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char) (in /usr/lib64/libstdc++.so.6.0.24)
==30863== by 0x40091B: main (in /path/to/prog)
==30863== Address 0x6011c0 is 0 bytes inside data symbol "_ZNSs4_Rep20_S_empty_rep_storageE"
它在 --std=c++14
及以下甚至启用 LTO 时也能正常工作。
那么问题是什么?两个编译器中 C++17 的 LTO 实现是否存在错误?或者只是 libstdc++
是用错误的标志编译的?我使用 opensuse 42.3,标准库是从存储库安装的。
它能以某种方式解决吗?
对于 gcc,这看起来像这个错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82172。
有许多解决方法,请参阅 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82172#c3。其中之一是使用 -D_GLIBCXX_USE_CXX11_ABI=1
:
g++ -D_GLIBCXX_USE_CXX11_ABI=1 --std=c++17 -flto prog.cpp
这里也有同样的问题
按照 ks1322 的链接,我终于找到了 binutils 错误 page,它 描述了问题的根源。
该错误最终在 binutils 2.30 中得到修复,并反向移植到 binutils 2.29 和 2.28。更新到 binutils 2.29 修复了测试示例,但我的实际构建仍然受到影响。
我发现我的项目依赖项之一(我使用 CMake 和 external project module)是在没有 LTO 的情况下构建的。将 -flto
添加到所有依赖项并使用适当的归档程序(即 gcc-ar
而不是 ar
)完全解决了问题。