zoukankan      html  css  js  c++  java
  • global constructor

    HQ在要求我们修改code style后,又让我检查并去掉"global constructor"。

    第一次听说这玩意,就研究了一下。发现网上有人讨论的很精彩,就记下来。

    “global constructors导致so的md5不一致”

    “今天遇到一个奇怪的问题,同一个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都是不一样的。”

    “感觉不应该这样的,对于确定的源代码,产生的binary一定是要一致的,不然不利于重现调试问题:假设一个build在客户机器上出现问题,在core file里找到相应的symbol, 但在公司开发环境只有source codes, binary 只能重新build. 如果binary每次都是产生不同的symbol name,那在原始build上的symbol name相当于没用了。杯具。”

    “我也遇到过这种情况,环境是 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,缺点是出了问题没法调试了。”

    “越来越有意思了,我在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说的这种情况。”

    “实验结果符合预期:
    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一般也不会变化那么快吧。当然你要是有更好的办法我更高兴啊,呵呵。”

    “恩,不过他提出的4种方法却不太好改:
    四种解决办法:
    1. 再定义一个无用的全局符号,比较丑陋。
    2. 如果再头文件中只需要声明不需要定义,改为 include iosfwd。
    3. 既然该文件是空的,那就不编译不链接好了。
    4. 对最终的可执行文件做 strip,缺点是出了问题没法调试了。
    工 程复杂了,第1/2种都不好改,而我遇到的情况并不是说是空文件,第4种就更不好改了。pee你提出的-frandom-seed倒是也可以解 roachcock的情况,只是需要给不同的参数太麻烦,正在尝试在scons里边动态修改-frandom-seed的值,让每个文件编译的时候都有不 同的-frandom-seed值,比如文件名。”

  • 相关阅读:
    有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
    C编程实现2的1000次方(使程序中的n=1000即可)
    有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面m个数。
    有一个字符串,内有若干字符,输入一个字符,要求程序将字符串中该字符删去。
    使用静态变量的方法求n!
    数组排序:冒泡法和选择法
    使用函数的递归调用来解决Hanoi(汉诺)塔问题。
    VC5509的通用GEL代码
    字、字节和位的关系
    安装JDK后环境变量的配置
  • 原文地址:https://www.cnblogs.com/chutianyao/p/3546184.html
Copyright © 2011-2022 走看看