HQ在要求咱們修改code style後,又讓我檢查並去掉"global constructor"。php
第一次據說這玩意,就研究了一下。發現網上有人討論的很精彩,就記下來。ios
「global constructors致使so的md5不一致」c++
「今天遇到一個奇怪的問題,同一個svn tag下的代碼,co幾份,每一份編譯幾回,scons出來的so的md5不一致,並且同一個目錄下的so和obj文件編譯幾回的md5也是不同的,但 同一個目錄下的so和obj大小是一致的,不一樣目錄的so和obj是不必定同樣的,不同的obj文件通常就是大8個字節。
對於md5不一致的so和obj,我nm | c++filt了一下,就是幾個global constructors不一致。查了一些資料,還沒徹底弄清楚,就發到版上來問問。
不 一致的地方是:global constructors keyed to _ZN102_GLOBAL__N_build_release64_src_text_feature_OrderFieldMatchCompletenessExtractor.cpp_00000000_CA317E552_1E, 主要是「CA317E552」這一段不太同樣,以前懷疑是編譯時帶了--export-dynamic的問題,去掉之後也無論用,不知道是怎麼回事,還請 各位大俠幫忙看看。先行謝過。
ps:是同一份代碼,同一臺機器,同一個帳戶,甚至是同一個目錄編譯出來的so的md5都是不同的。」dom
「感受不該該這樣的,對於肯定的源代碼,產生的binary必定是要一致的,否則不利於重現調試問題:假設一個build在客戶機器上出現問題,在core file裏找到相應的symbol, 但在公司開發環境只有source codes, binary 只能從新build. 若是binary每次都是產生不一樣的symbol name,那在原始build上的symbol name至關於沒用了。杯具。」svn
「我也遇到過這種狀況,環境是 gcc 4.1.2,貼一下當年的分析:
每次編譯 md5sum 都變化的緣由
每次編譯 md5sum 都變化,幫同事查了一下,過程和結果以下:
1. 兩次編譯,保留全部的 .o
2. 比較發現有一個.o變了
3. objdump -d 反彙編兩次的 .o 發現結果中有一個符號的名字每次都變,懷疑是C++全局對象動態初始化代碼形成的。
4. 打開對應的 .cpp,沒發現問題,可是該文件除了包含一些頭文件外,實際爲空文件。
5. 二分法逐漸去除包含的頭文件,發現了第一個頭文件依然會形成每次編譯 md5sum 都變化
6. 打開該頭文件,發現有 #include <iostream>,懷疑是它形成的
7. 寫一個空的 a.cpp,#include <iostream>,每次編譯都變化
8. 打開 /usr/include/c++/4.1.2/iostream,把他的內容抄到 a.cpp,每次編譯都變化;
去掉其中的 static ios_base::Init __ioinit; 再也不變化,問題緣由找到。
簡單重現:
$ cat a.cpp
extern int foo();
static int n = foo();
//int foo(){}
最後一行的存在與否決定是否會變化:
$ cat a.cpp; g++ -c a.cpp && { md5sum a.o; objdump -d a.o; nm a.o; }
比較:
保留 foo 定義時生成的代碼
0000002e <_GLOBAL__I__Z3foov>:
2e: 55 push %ebp
2f: 89 e5 mov %esp,%ebp
31: ba ff ff 00 00 mov $0xffff,%edx
36: b8 01 00 00 00 mov $0x1,%eax
3b: e8 c6 ff ff ff call 6 <_Z41__static_initialization_and_destruction_0ii>
40: 5d pop %ebp
41: c3 ret
去調保留 foo 定義時生成的代碼
00000028 <_GLOBAL__I_a.cpp_00000000_489C834E>:
28: 55 push %ebp
29: 89 e5 mov %esp,%ebp
2b: 83 ec 08 sub $0x8,%esp
2e: ba ff ff 00 00 mov $0xffff,%edx
33: b8 01 00 00 00 mov $0x1,%eax
38: e8 c3 ff ff ff call 0 <_Z41__static_initialization_and_destruction_0ii>
3d: c9 leave
3e: c3 ret
緣由:
gcc 靜態全局對象動態初始化要生成額外的代碼,當其所在的 translation unit 沒有其餘全局符號生成的時候,他須要生成更能保證惟一性的名字。
四種解決辦法:
1. 再定義一個無用的全局符號,比較醜陋。
2. 若是再頭文件中只須要聲明不須要定義,改成 include iosfwd。
3. 既然該文件是空的,那就不編譯不連接好了。
3. 對最終的可執行文件作 strip,缺點是出了問題無法調試了。」ui
「愈來愈有意思了,我在gcc的官網找到這麼一個信息,可能有點關係:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10591
其中有一句是這麼說的「since at the moment we give
things in an anonymous namespace a random name to avoid exactly this kind of
problem.」
並提到相關bug在gcc 4.2.0 fix。我理解這是否是致使4.4.4表現ok的緣由。
謝謝你們,我再看看RoachCock說的這種狀況。」this
「實驗結果符合預期:
cat a.cpp; g++ -c a.cpp && { md5sum a.o; nm a.o|c++filt; }
extern int foo();
static int n = foo();
31e8db85138895c2a1548220200fa78b a.o
000000000000002a t global constructors keyed to a.cpp_00000000_68145A2A
U foo()
0000000000000000 t __static_initialization_and_destruction_0(int, int)
U __gxx_personality_v0
0000000000000000 b n
cat a.cpp; g++ -c a.cpp && { md5sum a.o; nm a.o|c++filt; }
extern int foo();
static int n = foo();
321474f70890edd158b4068a0af0f570 a.o
000000000000002a t global constructors keyed to a.cpp_00000000_C9C90818
U foo()
0000000000000000 t __static_initialization_and_destruction_0(int, int)
U __gxx_personality_v0
0000000000000000 b n
==================華麗的分割線================
cat a.cpp; g++ -c a.cpp && { md5sum a.o; nm a.o|c++filt; }
extern int foo();
static int n = foo();
int aa;
71d646621029a7e832b7f4a76ed3a5b4 a.o
000000000000002a t global constructors keyed to aa
U foo()
0000000000000000 t __static_initialization_and_destruction_0(int, int)
U __gxx_personality_v0
0000000000000000 B aa
0000000000000004 b n
cat a.cpp; g++ -c a.cpp && { md5sum a.o; nm a.o|c++filt; }
extern int foo();
static int n = foo();
int aa;
71d646621029a7e832b7f4a76ed3a5b4 a.o
000000000000002a t global constructors keyed to aa
U foo()
0000000000000000 t __static_initialization_and_destruction_0(int, int)
U __gxx_personality_v0
0000000000000000 B aa
0000000000000004 b n
to坑王:比較md5sum是不少地方都在用的土辦法,看2個版本是否是一致的。
build號自動加1只要不是體如今源代碼級別上的就沒太多問題啊,soname通常也不會變化那麼快吧。固然你要是有更好的辦法我更高興啊,呵呵。」spa
「恩,不過他提出的4種方法卻不太好改:
四種解決辦法:
1. 再定義一個無用的全局符號,比較醜陋。
2. 若是再頭文件中只須要聲明不須要定義,改成 include iosfwd。
3. 既然該文件是空的,那就不編譯不連接好了。
4. 對最終的可執行文件作 strip,缺點是出了問題無法調試了。
工 程複雜了,第1/2種都很差改,而我遇到的狀況並非說是空文件,第4種就更很差改了。pee你提出的-frandom-seed卻是也能夠解 roachcock的狀況,只是須要給不一樣的參數太麻煩,正在嘗試在scons裏邊動態修改-frandom-seed的值,讓每一個文件編譯的時候都有不 同的-frandom-seed值,好比文件名。」3d