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值,比如文件名。”

global constructor的更多相关文章

  1. JavaScript Patterns 5.5 Sandbox Pattern

    Drawbacks of the namespacing pattern • Reliance on a single global variable to be the application’s ...

  2. Java Code Examples for javax.servlet.http.Part

    http://www.programcreek.com/java-api-examples/index.php?api=javax.servlet.http.Part The following ar ...

  3. iOS:消除项目中警告

    引言: 在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能执行之类的警告. 有代码洁癖的孩子们很想消除他们, 今天就让我们来一次Fuck 警告!! 首先学会基本的语句: ...

  4. 【转】clang warning 警告清单(备查,建议直接command + F 速查 )

    Warning Message -WCFString-literal input conversion stopped due to an input byte that does not belon ...

  5. 使用#pragma阻止一些warnings

    这篇博客的内容都是记的网上的.是流水账.只是记录下来以便日后之有,避免每次重新google. #pragma除了可以用来把不同功能的代码进行分隔组织外还可以用来disable一些warnings.这在 ...

  6. iOS -- warnings

    Semantic Warnings Warning Message -WCFString-literal input conversion stopped due to an input byte t ...

  7. IOS 警告 收集

    Semantic Warnings Warning Message -WCFString-literal input conversion stopped due to an input byte t ...

  8. ios deprecated 警告消除 强迫症的选择

    #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" ...

  9. iOS编程 手动忽略clang编译器警告

    在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能运行之类的警告. 有代码洁癖的孩子们非常想消除他们, 今天就让我们来一次Fuck 警告.! 首先学会主要的语句 #pra ...

随机推荐

  1. [BZOJ5305][Haoi2018]苹果树 组合数

    题目描述 小 C 在自己家的花园里种了一棵苹果树, 树上每个结点都有恰好两个分支. 经过细心的观察, 小 C 发现每一天这棵树都会生长出一个新的结点. 第一天的时候, 果树会长出一个根结点, 以后每一 ...

  2. 六十一 Web开发 使用Web框架

    由于用Python开发一个Web框架十分容易,所以Python有上百个开源的Web框架.这里我们先不讨论各种Web框架的优缺点,直接选择一个比较流行的Web框架——Flask来使用. 用Flask编写 ...

  3. centos 7 mini版中安装Python3.x

    首先了解几句Linux命令是必须的.例如 ls, vi, wget, rm, mv, cd, su, sudo, chmod, tar等等一些常用的语句命令是有必要知道它的用法的. 安装Python3 ...

  4. 洛谷P1414 又是毕业季 [数论]

    题目传送门 又是毕业季 题目背景 “叮铃铃铃”,随着高考最后一科结考铃声的敲响,三年青春时光顿时凝固于此刻.毕业的欣喜怎敌那离别的不舍,憧憬着未来仍毋忘逝去的歌.1000多个日夜的欢笑和泪水,全凝聚在 ...

  5. Java介绍

    Java简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称.由James Gosling和同事们共同研发,并在1995年正式推 ...

  6. coreseek 段错误 (core dumped) 问题

    coreseek建立索引出现上面问题经过测试发现有下面几个原因: 1. 分词配置文件不存在  uni.lib 2. uni.lib配置文件格式不正确

  7. Extjs MVC模式

    最近在学习Extjs,发现他可以使用MVC模式,不但可以组织代码,而且可以 减少实现的内容,模型(Models)和控制器(Controllers)也被引入其中. Model模型是字段和它们的数据的集合 ...

  8. Standard Java集合类问题待整理

    1. HashSet.LinkedHashSet和TreeSet HashSet采用散列函数对元素进行排序,是专门为快速查询而设计的.存入HashSet的对象必须定义hashCode方法. TreeS ...

  9. Java多线程-run方法与start方法的区别

    package com.interview; /** * java多线程的两种实现方式以及run.start方法的区别 * @author MEI.LIU * */ public class Thre ...

  10. JZYZOJ1537 [haoi2014]贴海报

    http://172.20.6.3/Problem_Show.asp?id=1537 用的方法叫作浮水法,实质是递归自下而上判断一个区间有没有覆盖,O(n^2)感觉也没有很实用. 前几年的haoi怎么 ...