global constructor

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

相关文章
相关标签/搜索