专栏首页蓝天小心两个共享库共用同一个静态库

小心两个共享库共用同一个静态库

小心两个共享库共用同一个静态库.pdf 注:以下内容仅针对Linux/GCC环境,不涵盖Windows,包括Cygwin环境。 下载测试代码:

x.zip(和帖子的略不同,x.zip包中的全局变量是个类对象,带有构造和析构函数) 推荐阅读:http://blog.chinaunix.net/uid-20682147-id-351108.htmlLinux上制作可执行的共享库示例 问1:如果测试中的全局变量global_var是个带构造和析构的类对象,会如何?(答案在最后) 问2:如果使用-fPIE替代-fPIC编译链接,会是什么结果了? 位置无关代码(PIC)对常量和函数入口地址的操作都是采用基于基寄存器(base register)BASE+ 偏移量的相对地址的寻址方式,即使程序被装载到内存中的不同地址(即 BASE值不同),而偏移量是不变的,所以程序仍然可以找到正确的入口地址或者常量。 为何要小心?原因是在使用dlopen动态加载共享库时,如果静态库中包含有全局变量,可能会出现名同地址不同的全局变量。 解决办法:总是使用RTLD_GLOBAL加载共享库,而不是RTLD_LOCAL。以下是测试程序: Makefile

 			# test shared libraries use static a same static library 		
 			# the global variables defined at static library have the same address 		
 
 			all: x libshared_lib1.so libshared_lib2.so 		
 			x: x.cpp #libstatic_lib.a #libshared_lib1.so #libshared_lib2.so 		
 			g++ -g -o $@ $^ -ldl 		
 
 			libstatic_lib.a: static_lib.h static_lib.cpp 		
 			g++ -g -fPIC -c static_lib.cpp -I. 		
 			ar cr $@ static_lib.o 		
 
 			libshared_lib1.so: shared_lib1.cpp libstatic_lib.a 		
 			g++ -g -fPIC -shared -o $@ $^ -I. 		
 
 			libshared_lib2.so: shared_lib2.cpp libstatic_lib.a 		
 			g++ -g -fPIC -shared -o $@ $^ -I. 		
 
 			clean: 		
 			rm -f static_lib.o libstatic_lib.a 		
 			rm -f shared_lib1.o libshared_lib1.so 		
 			rm -f shared_lib2.o libshared_lib2.so 		
 			rm -f x 		

 
 测试程序x.cpp
 
 			#include <dlfcn.h>
 
 			#include <stdio.h>
 
 			#include <stdlib.h>
 
 
 
 			extern void call_foo(const char* name, int load_flag);
 
 int main()
 
 { 
 int flag = RTLD_GLOBAL|RTLD_NOW;  // 如果是RTLD_GLOBAL则静态库中定义的全局变量在共享库中名同地址也同
 
 //int flag = RTLD_LOCAL|RTLD_NOW;  // 如果是RTLD_LOCAL则静态库中定义的全局变量在共享库中名同地址不同
 
 
 
 			        call_foo("./libshared_lib1.so", flag);
 
 			        call_foo("./libshared_lib2.so", flag);
 
 
 
 			        return 0;
 
 }
 
 
 
 // RTLD_NOW
 
 // RTLD_LAZY
 
 // RTLD_GLOBAL
 
 // RTLD_LOCAL
 
 			void call_foo(const char* name, int load_flag)
 
 {
 
 			        char *error;
 
 			        void (*foo)();
 
 
 
 			        void* handle = dlopen(name, load_flag);
 
 if (NULL == handle)
 
 {
 
 			                fprintf (stderr, "%s\n", dlerror());
 
 exit(1);
 
 }
 
 
 
 dlerror(); /* Clear any existing error */
 
 *(void **) (&foo) = dlsym(handle, "foo");
 
 if ((error = dlerror()) != NULL)
 
 {
 
 			                fprintf (stderr, "%s\n", error);
 
 exit(1);
 
 }
 
 
 
 (*foo)();
 
 } 

静态库头文件static_lib.h

  1. extern int global_var;

静态库实现文件static_lib.cpp

 			#include <stdio.h>
 
 int global_var = 2013; 

第1个共享库实现文件shared_lib1.cpp 

 			#include "static_lib.h"
 
 			#include <stdio.h>
 
 
 
 			extern "C" void foo()
 
 {
 
 			        global_var = 1111;
 
 			        printf("%p 1-> %d\n", &global_var, global_var);
 
 } 

 
 第2个共享库实现文件shared_lib2.cpp
 
 			#include "static_lib.h"
 
 			#include <stdio.h>
 
 
 
 			extern "C" void foo()
 
 {
 
 			        printf("%p 2-> %d\n", &global_var, global_var);
 
 } 

测试环境: x86_64 x86_64 GNU/Linux 2.6.16 附: 1)如果你想覆盖系统调用,可以使用LD_PRELOAD或/etc/ld.so.preload,也可进一步了解RTLD_NEXT; 2)静态库顺序关系:假设X.a依赖Z.a,则顺序为X.a Z.a,亦即被依赖的排在后面,否则链接时会报某些符号找不到(详细请参见:链接静态库的顺序问题)。 答:结果是即使以RTLD_GLOBAL方式加载,都会出现两次构造和析构调用,如果是RTLD_GLOBAL方式,地址仍然相同,也就是同一个对象执行了两次构造和析构,后果当然是非常危险。运行测试代码x.zip即可得到验证。 段表(Section Table) 一个描述文件中各个段的数组 .code/.text 代码段 .data 段保存的是那些已经初始化了的全局静态变量和局部静态变量 .rodata/.rodata1 段存放的是只读数据,一般是程序里面的只读变量(如const修饰的变量)和字符串常量 .bss 段存放的是未初始化的全局变量和局部静态变量 .plt/.got 段动态链接的跳转表和全局入口表 .symtab 符号表(Symbol Table) .strtab 字符串表(String Table),用于存储ELF文件中用到的各种字符串 .init/.fini 程序初始化与终结代码段 .note 额外的编译器信息。比如程序的公司名、发布版本号等 .line 调试时的行号表,即源代码行号与编译后指令的对应表 .hash 符号哈希表 .dynamic 动态链接信息 .debug 调试信息 .comment 存放的是编译器版本信息,比如字符串:”GCC: (GNU) 4.2.0” 自定义段 GCC提供了一个扩展机制,使得程序员可以指定变量所处的段: 1.__attribute__((section("FOO"))) int global = 42; 2.__attribute__((section("BAR"))) void foo() { } 在全局变量或函数之前加上"__attribute__((section("name")))"属性就可以把相应的变量或函数放到以"name"作为段名的段中。

  • 如果被依赖的不是静态库,而是共享库,则无论何种方式都不存在问题
  • 为何即使RTLD_GLOBAL加载,也会执行两次构造和析构?原因是两个共享库存在相同的代码段,如果被依赖的是共享库,则不存在这个问题
 				-Wl的使用 			
 
 				-Wl表示后面的参数传递给链接器,其中l是linker的意思。 			
 
 				链接时指定共享库的搜索路径(类似于设置LD_LIBRARY_PATH): 			
 				-Wl,-rpath=/usr/local/abc:/data/abc 			
 				以上也可以分开写: 			
 				-Wl,-rpath=/usr/local/abc -Wl,-rpath=/data/abc 			
 
 				部分库链接它的静态库,部分库链接它的共享库: 			
 				-Wl,-static -lb -Wl,-call_shared -la -lz 			
 
 				指定链接器: 			
 				-Wl,-dynamic-linker /lib/ld-linux.so.2 -e _so_start 			
 
 				指定导出的符号: 			
 				-Wl,--export-dynamic,--version-script,exports.lds 			
 
 				exports.lds的格式可以为: 			
 				{ 			
 				global: 			
 				foo; 			
 				}; 			
 
 				指定共享库的soname: 			
 				-Wl,--export-dynamic,--version-script,exports.lds,-soname=libqhttpd.so 			
 
 
 				-rpath 增加共享库搜索路径 			
 				--retain-symbols-file表示不丢弃未定义的符号和需要重定位的符号 			
 				--export-dynamic 创建一个动态连接的可执行程序时, 把所有的符号加到动态符号表中 			

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux下可以替换运行中的程序么?

    今天被朋友问及“Linux下可以替换运行中的程序么?”,以前依稀记得Linux下是可以的(而Windows就不让),于是随口答道“OK”。结果朋友发来一个执行结...

    一见
  • __cxa_call_unexpected原因

    #1  0xf73c4657 in raise () from /lib/libc.so.6

    一见
  • 当gdb看到一行行的??时,你要冷静!

    下面这段,初看一定会脑大,实际原因非常明确,所以遇到时要先观察,不一定是头大的问题。 gdb -p 1461 GNU gdb 6.6 Copyright (...

    一见
  • breakpad: Native crash 日志收集工具前言正题breakpad工作原理项目集成

    现在大部分应用都会有Java层的崩溃日志收集机制,一般就是程序crash后,展示一个上报界面,用户点击就上传了。 但是Native程序crash了,很少有做处...

    用户2930595
  • ​openssl Android编译指南

    打开根目录下的build.info, 注释下面几行, 在Line:590~594, 否则会有类似错误提示 ${LDCMD:-g++} ld: unknown o...

    望天
  • Ubuntu循环登录libGL error: fbConfigs swrast等

    Ubuntu16.04更新NVIDIA驱动后,无法进入桌面,使用vim .xsession-errors

    zhangrelay
  • FastDFS安装

    libfastcommon是 FastDFS 中的公共 C 函数库,基础环境。下载编译安装

    机械视角
  • WAS 8.5在HP-UX Itanium上无法图形化安装启动IIM之解 博客分类: JavaIBMWebSphere IBMWASIIM

    继之前写的“WAS 8.5在AIX上无法启动图形化概要管理工具WCT或PMT之解”之后

    阿敏总司令
  • 再谈Android动态链接库

    前不久,我们准备将自己开发的视频播放sdk提供给公司其他部门,在打包的时候,同事问了我一个问题,为什么我们打sdk的时候需要分别提供armeabi和arm64-...

    xiangzhihong
  • 微信小程序如何套用iconfont

    查看官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/

    达达前端

扫码关注云+社区

领取腾讯云代金券