全局符号

最近打算把linux下的一个c++程序以插件的形式实现,即主程序加载so功能模块的方式,在这个过程中,遇到了一个全局符号覆盖的问题,查了一些资料有了初步的了解,这里记录一下。

遇到的问题是:程序以dlopen方式加载so文件时,如果so文件和程序存在同名的全局变量,主程序的全局变量会so文件中的全局变量覆盖。

由此,引出的问题如下

1、同名全局变量覆盖与dlopen的打开方式是否有关?
2、与主程序和so文件的编译选项是否有关?
3、以静态库.a直接连接是否也存在这个现象?
4、以动态库.so直接链接是否也存在这个问题?
5、同名全局函数是否会被覆盖?

以下多来自网上的材料,这里整理一下

1、全局变量初始化

时间
根据 C++ 标准,全局变量的初始化要在 main 函数执行前完成,既有编译时,也可能会有运行时(seriously), 其中静态初始化执行先于动态初始化!

1、static initialization: 静态初始化指的是用常量来对变量进行初始化,主要包括 zero initialization 和 const initialization,静态初始化在程序加载的过程中完成,对简单类型(内建类型,POD等)来说,从具体实现上看,zero initialization 的变量会被保存在 bss 段,const initialization 的变量则放在 data 段内,程序加载即可完成初始化,这和 c 语言里的全局变量初始化基本是一致的。
2、dynamic initialization:动态初始化主要是指需要经过函数调用才能完成的初始化,比如说:int a = foo(),或者是复杂类型(类)的初始化(需要调用构造函数)等。这些变量的初始化会在 main 函数执行前由运行时调用相应的代码从而得以进行(函数内的 static 变量除外)。--貌似gcc不支持这种方式,未验证

顺序

1、同一个编译单元内的全局变量,初始化的顺序与声明顺序一致(销毁的顺序则反过来)
2、不同编译单元间的全局变量,c++ 标准并没有明确规定它们之间的初始化(销毁)顺序,因此实现上完全由编译器自己决定,一个比较普遍的认识是:不同编译单元间的全局变量的初始化顺序是不固定的,哪怕对同一个编译器,同一份代码来说,任意两次编译的结果都有可能不一样

以上内容主要来自 twoon c++ 全局变量初始化的一点总结 该博主的博客挺有意思的,mark一下~

2、动态库的初始化

动态在被加载时会进行内存映射,之后需要运行一些动态库的初始化函数构建动态库的运行环境,包括

(1)、动态库的构造和析构机制
void __attribute__ ((constructor)) my_init(void);
void __attribute__ ((destructor)) my_fini(void);
(2)、动态库的全局变量初始化
在C语言中,其全局变量保存在.data段,启动过程中,loader只是简单地使用mmap将数据段映射到内存中,这些全局变量只有在第一次使用到的时候才会为其分配物理内存,其在启动过程中不需要运行构造函数。在C++语言中,对于非内置类型的全局对象,系统要在main函数之前,运行其构造函数,完成全局变量的初始化。这将导致(1)减缓了进程或动态库的启动加载速度。(2)构造函数修改了类的成员变量,这会产生page fault减缓进程的启动速度;同时也会产生一些不必要的dirty page,造成内存上的浪费。

查找一个执行程序或动态库的全局变量

nm -f sysv hello | grep bss
1、在bss节,类型为OBJECT,不包含函数名,对象大小>4,基本可以认为是全局对象。
2、在bss节,类型为OBJECT,不包含函数名,对象大小=4,有可能是全局对象,需要进一步确认。

以上内容主要来自嵌入式Linux内存使用与性能优化这本书

3、动态库的符号加载问题

这里直接上结论吧
1、符号查找

如果一个动态库依赖了一些外部符号(位于其他动态库甚至应用程序中)。
(1)在直接链接这个动态库的时候,可以把依赖的其他库也直接链接上。
(2)对于动态加载的库,要保证在加载该库时,进程中加载的其他动态库里已经存在该符号。例如,可以通过LD_PRELOAD环境变量可以让一个进程先加载指定的动态库

2、符号覆盖

同名全局函数
(1)直接link动态库时,整个程序运行的时候符号会发生覆盖,只有一个符号被使用。更复杂的情况中,多个动态库和程序都有相同的符号,情况也是一样,会发生符号覆盖。如果程序里没有这个符号,而多个动态库里有相同的符号,也会覆盖。
(2)动态载入的动态库,在dlopen打开时默认使用RTLD_LOCAL参数因此,其函数符号不会出现覆盖情况;但如果采用RTLD_GLOBAL参数,则和直接link一样会出现同名函数符号的覆盖
(3)一种特殊的情况,-rdynamic,指示连接器把所有符号(不包括静态符号,比如被static修饰的函数)都添加到动态符号表.dynsym表里,以便dlopen()backtrace()等函数使用。如果执行程序在链接时采用了该参数,则执行程序在dlopen加载动态库时(无论RTLD_LOCAL还是RTLD_GLOBAL),动态库内与执行程序同名的函数符号都将被执行程序中的函数符号覆盖。

同名全局变量
(1)与全局函数不同,执行程序和.so文件全局变量符号重复时,无论是直接链接还是动态链接,都会发生覆盖的情况,最后加载的动态库中的全局变量会冲掉之前加载的全局变量,全局变量会被初始化/释放(有可能导致double free)两次,需谨慎。
(2)对于不同的so文件链接同一个.a静态库,对于该静态库中的全局变量有如下两种情况:

  • 直接link的,总是用同一个变量。
  • 动态加载dlopen,依赖dlopen()的flag:(存疑,待验证)
    如果是RTLD_LOCAL(默认),各个so会使用各自的.a里的变量。
    如果用RTLD_GLOBAL,就跟直接link一样,用同一个变量了。

以上内容主要来自linux动态库的种种要点关于动态链接库(dynamic library)里static变量和函数的问题gcc选项-g与-rdynamic的异同

4、补充-C编译器对多重定义的全局变量符号的解析和链接

在编译阶段,编译器将全局符号信息隐含地编码在可重定位目标文件的符号表里。这里有个“强符号(strong)”“弱符号(weak)”的概念——前者指的是定义并且初始化了的变量,后者指的是未定义或者定义但未初始化的变量。当符号被多重定义时,GNU链接器(ld)使用以下规则决议:

1、不允许出现多个相同强符号。
2、如果有一个强符号和多个弱符号,则选择强符号。
3、如果有多个弱符号,那么先决议到size最大的那个,如果同样大小,则按照链接顺序选择第一个。

上面所说的是在直接链接时的情况;在动态加载时,“允许”执行程序和so文件出现多个相同强符号,根据前面的结论,这种情况下会出现全局符号覆盖的情况。

以上内容来自C语言全局变量那些事儿

5、结论

最后对开始的问题进行简单整理一下(部分结论未验证,很可能出错,待验证后,具体再补充

1、同名全局变量覆盖与dlopen的打开方式是否有关?
-》执行程序和so之间的全局变量覆盖与dlopen的参数无关,无论直接链接还是动态加载都会存在覆盖问题;so文件之间的全局变量覆盖问题(待验证)

2、与主程序和so文件的编译选项是否有关?
-》全局变量的覆盖问题和编译选项无关,覆盖前提和条件如上所述
全局函数的覆盖问题与编译选项-rdynamic有关

3、以静态库.a直接连接是否也存在这个现象?
-》当一个符号在多个目标文件(.o)里同时出现时, LD报错. 提示符号多重定义.
当一个符号在多个静态库(.a)里同时出现时, LD不报错, 以第一个遇到的为准. 并且不会有任何warning提示。

4、以动态库.so直接链接是否也存在这个问题?
-》会覆盖,并且是后面覆盖前面

5、同名全局函数是否会被覆盖?
-》(1)直接link动态库时发生覆盖;(2)动态载入的动态库,与dlopen打开时参数RTLD_LOCAL、RTLD_GLOBAL有关(3)执行程序在链接时采用了-rdynamic,则执行程序在dlopen加载动态库时,.so与执行程序同名的函数符号都将被执行程序中的函数符号覆盖。

reference

C语言全局变量那些事儿
linux动态库的种种要点
关于动态链接库(dynamic library)里static变量和函数的问题
嵌入式Linux内存使用与性能优化
twoon c++ 全局变量初始化的一点总结
gcc选项-g与-rdynamic的异同
GCC/LD编译链接潜规则 (第一弹) : 当一个符号被多重定义时

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容