升级GCC版本导致的程序奔溃:代码不规范
文章目录
同样的业务代码,centos7 编译发布,运行正常,切换到 centos8 编译发布,程序发生崩溃。
问题仅发生在 Release 模式,Debug 模式编译发布,程序都是正常的。
第一次排查此类问题,组里断断续续,排查了三天,最终定位到问题。
结论
函数没有定义返回值,Release 模式下高版本gcc编译后有了更多的优化,导致了未知的执行逻辑,发生崩溃,GCC 的编译告警,记得处理,老项目不也建议全部屏蔽,屏蔽部分即可。
环境描述
centos7 gcc 版本
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)
Copyright © 2015 Free Software Foundation, Inc.
本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;
包括没有适销性和某一专用目的下的适用性担保。
centos8 gcc 版本
gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
崩溃现场
此堆栈为问题定位以后,单独编写的测试代码,实际业务代码堆栈更加复杂。
[New LWP 1385902]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./pstack_main'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007ffe894b4420 in ?? ()
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-251.el8.x86_64 libgcc-8.5.0-21.el8.x86_64 libstdc++-8.5.0-21.el8.x86_64
(gdb) bt
#0 0x00007ffe894b4420 in ?? ()
#1 0x00000000004008e9 in main ()
看到这个堆栈,是不是没有什么头绪,崩溃的函数栈居然是个问号,下面是完整代码,你分析出来了问题吗?
#include <iostream>
#include <map>
int test()
{
std::cout << "1" << std::endl;
}
int main() {
test();
return 0;
}
编译告警
组内项目的基础 Cmake 脚本,屏蔽了很多告警信息,问题恰恰就是下面这个告警: 函数没有定义返回值,Release 模式下高版本gcc编译后有了更多的优化,导致了未知的执行逻辑,发生崩溃
[build] /root/pstack/main.cpp: In function ‘int test()’:
[build] /root/pstack/main.cpp:7:1: warning: no return statement in function returning non-void [-Wreturn-type]
[build] }
[build] ^
[build] [100%] Linking CXX executable pstack_main
[build] [100%] Built target pstack_main
[driver] Build completed: 00:00:00.554
[build] Build finished with exit code 0
汇编代码
仅供参考,没有具体的分析,可以很明显看到,高版本的 GCC 做了更多的优化,大幅减少了汇编的代码量。
低版本 GCC-4.8.5 汇编代码:
.LC0:
.string "1"
test():
pushq %rbx
movl $1, %edx
movl $.LC0, %esi
movl $std::cout, %edi
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
movq std::cout(%rip), %rax
movq -24(%rax), %rax
movq std::cout+240(%rax), %rbx
testq %rbx, %rbx
je .L7
cmpb $0, 56(%rbx)
je .L3
movzbl 67(%rbx), %eax
.L4:
movsbl %al, %esi
movl $std::cout, %edi
call std::basic_ostream<char, std::char_traits<char> >::put(char)
movq %rax, %rdi
call std::basic_ostream<char, std::char_traits<char> >::flush()
popq %rbx
ret
.L3:
movq %rbx, %rdi
call std::ctype<char>::_M_widen_init() const
movq (%rbx), %rax
movl $10, %esi
movq %rbx, %rdi
call *48(%rax)
jmp .L4
.L7:
call std::__throw_bad_cast()
main:
subq $8, %rsp
call test()
xorl %eax, %eax
addq $8, %rsp
ret
_GLOBAL__sub_I_test():
subq $8, %rsp
movl $_ZStL8__ioinit, %edi
call std::ios_base::Init::Init() [complete object constructor]
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
jmp __cxa_atexit
高版本 GCC-8.5.0 汇编代码:
.LC0:
.string "1"
test():
movl $_ZSt4cout, %edi
subq $8, %rsp
movl $1, %edx
movl $.LC0, %esi
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
movl $_ZSt4cout, %edi
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
main:
subq $8, %rsp
call test()
_GLOBAL__sub_I_test():
subq $8, %rsp
movl $_ZStL8__ioinit, %edi
call std::ios_base::Init::Init() [complete object constructor]
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
jmp __cxa_atexit