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值,比如文件名。”
global constructor的更多相关文章
- JavaScript Patterns 5.5 Sandbox Pattern
Drawbacks of the namespacing pattern • Reliance on a single global variable to be the application’s ...
- 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 ...
- iOS:消除项目中警告
引言: 在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能执行之类的警告. 有代码洁癖的孩子们很想消除他们, 今天就让我们来一次Fuck 警告!! 首先学会基本的语句: ...
- 【转】clang warning 警告清单(备查,建议直接command + F 速查 )
Warning Message -WCFString-literal input conversion stopped due to an input byte that does not belon ...
- 使用#pragma阻止一些warnings
这篇博客的内容都是记的网上的.是流水账.只是记录下来以便日后之有,避免每次重新google. #pragma除了可以用来把不同功能的代码进行分隔组织外还可以用来disable一些warnings.这在 ...
- iOS -- warnings
Semantic Warnings Warning Message -WCFString-literal input conversion stopped due to an input byte t ...
- IOS 警告 收集
Semantic Warnings Warning Message -WCFString-literal input conversion stopped due to an input byte t ...
- ios deprecated 警告消除 强迫症的选择
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" ...
- iOS编程 手动忽略clang编译器警告
在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能运行之类的警告. 有代码洁癖的孩子们非常想消除他们, 今天就让我们来一次Fuck 警告.! 首先学会主要的语句 #pra ...
随机推荐
- mysql中json数据的拼接方式
SELECT CONCAT('[', GROUP_CONCAT( CONCAT('{"id":"',STRUCTURE_ID,'"'),',', CONCAT( ...
- Thinking in java基础之集合框架(转载)
集合简介(容器)把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合,例如这里有20个苹果,我们把每一个苹果当成一个东西(一个对象),然后我们借用袋子把这20个苹果装起来,而这个袋子就是集合(也叫 ...
- lr手工添加关联函数的步骤:
点击“确定”后: 如何修改已经创建好的关联规则:
- 3.spark streaming Job 架构和容错解析
一.Spark streaming Job 架构 SparkStreaming框架会自动启动Job并每隔BatchDuration时间会自动触发Job的调用. Spark Streaming的Job ...
- Codeforces 1 C. Ancient Berland Circus-几何数学题+浮点数求gcd ( Codeforces Beta Round #1)
C. Ancient Berland Circus time limit per test 2 seconds memory limit per test 64 megabytes input sta ...
- Flask实战第51天:cms添加轮播图后端代码逻辑完成
首先,我们需要给轮播图设计一张表,因为轮播图前端要展示,CMS要管理,所以我们在apps下新建个models.py 编辑apps.models.py from exts import db from ...
- If you want to allow applications containing errors to be published on the server
If you want to allow applications containing errors to be published on the server, enable the Allow ...
- Requirejs快速使用
Requirejs是一个简单,实用,强大的前端模块化js库,它遵循AMD这一客户端模块定义规范,使用它能够使我们前端代码更加清晰高效加载,摆脱了以前前端代码杂乱无序的状态. 使用步骤如下: 1.下载r ...
- hdu 5961 传递 (2016ccpc 合肥站 A题)
传递 Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submiss ...
- zigbee控制ADAM4150
任务名称:zigbee节点控制ADAM4150 实验现象:通过zigbee节点上的节点控制ADAM 注意点:控制ADAM4150的波特率必须是9600,否则会出现错误 核心代码 1.寄存器配置 voi ...