同一のビジネスコードの場合、プログラムは CentOS 7 環境で正常にコンパイルされ実行されたが、CentOS 8 に切り替えて更新版 GCC でコンパイルするとクラッシュした。問題は Release 模式 の場合にのみ発生し、Debug 模式 では全く問題なかった。今回初めてこのような事態に遭遇したが、3 日間の調査の結果、ようやく原因を突き止めた。
問題特定
徹底的な調査の結果、問題の根本原因は 函数缺少返回值 にありました。リリースモードでは、GCC の新バージョンがより多くの最適化を行うため、元々明示的な戻り値を持たない関数が実行中に予期せぬ動作を起こし、クラッシュを引き起こしました。結論として、编译器的警告不容忽视,尤其是在老项目中,部分警告可能被无视,但也应当避免屏蔽所有警告 です。
環境説明
-
申し訳ありませんが、翻訳する中国語のテキストが提供されていません。テキストを提供してください。
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39) Copyright © 2015 Free Software Foundation, Inc.
-
申し訳ありませんが、翻訳する中国語のテキストが提供されていません。テキストを提供してください。
gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21) Copyright (C) 2018 Free Software Foundation, Inc.
クラッシュ現象
プログラムクラッシュのスタックを解析した結果、以下のスタック情報が見られました。
[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 ?? ()
(gdb) bt
#0 0x00007ffe894b4420 in ?? ()
#1 0x00000000004008e9 in main ()
このスタックは直感的ではなく、クラッシュした関数のスタック情報が INLINE_CODE_0 と表示されているため、問題の特定がより複雑になっています。
コード例
問題をより良く理解するために、クラッシュを再現する最小限のコード例を以下に示します。
#include <iostream>
#include <map>
int test() {
std::cout << "1" << std::endl;
}
int main() {
test();
return 0;
}
そのコード内の test()
函数显然没有显式返回一个值,而它的返回类型是 int
。根据 C++ 规范,当一个函数声明为 INLINE_CODE_2 型では、必ず値を返す必要がある。さもなくば、未定義の動作を引き起こす可能性がある。
コンパイル警告
プロジェクトにおいて、CMakeスクリプトが多くのコンパイル時の警告を抑制しており、その中には以下の警告が含まれています。
/root/pstack/main.cpp: In function ‘int test()’:
/root/pstack/main.cpp:7:1: warning: no return statement in function returning non-void [-Wreturn-type]
この警告は、__INLINE_CODE_0__関数が値を返さないことを示しており、これが問題の根本原因です。高バージョンGCC(例えば8.5.0)は、コードを最適化する際に、このような未定義の振る舞いに対して不安定な最適化を行い、プログラムをクラッシュさせる可能性があります。
アセンブリコードの差異
GCCコンパイラ最適化の差異を説明するため、異なるバージョンのGCCが生成するアセンブリコードを比較しました。
- 申し訳ありませんが、翻訳する中国語のテキストが提供されていません。テキストを提供してください。
アセンブリコードは冗長であり、標準出力ストリーム(std::cout
)的处理逻辑。这表明编译器进行了更保守的优化,未对 test()
関数における戻り値の欠落問題など)に対する過剰な最適化が含まれているため、クラッシュを回避できた可能性がある。
- 申し訳ありませんが、翻訳する中国語のテキストが提供されていません。テキストを提供してください。
新しいGCCでは、より多くの最適化が行われ、コード量が削減されました。しかし、この最適化により、戻り値がない関数の実行時に動作が不安定になり、プログラムがクラッシュする可能性があります。
結論
今回の問題の調査を通じて、C++において、函数返回值必须明确(特に__INLINE_CODE_0__ 时,必须提供一个返回值。对于使用较旧版本编译器的项目,升级到新版本的 GCC 时,可能会遇到更多的优化和更严格的警告机制。因此,我们建议在编译时 **関数宣言の場合)、すべての警告を無視するのではなく、選択的に処理することが重要であると深く認識しました。特に、関数戻り値や型の一致など、一般的な問題には注意が必要です。
最終、INLINE_CODE_0 関数に返り値を追加することで問題が解決し、プログラムは正常に動作するようになった。