C++程序同时使用同一个库的不同版本,为什么运行时崩溃了?( 二 )

此时,通过 readelf 命令读取 test.o 和 libtest.a,输出是一致的 。
$ readelf -a test.oELF Header:Magic:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class:ELF64Data:2's complement, little endianVersion:1 (current)OS/ABI:UNIX - System VABI Version:0Type:REL (Relocatable file)machine:Advanced Micro Devices X86-64Version:0x1Entry point address:0x0Start of program headers:0 (bytes into file)Start of section headers:680 (bytes into file)Flags:0x0Size of this header:64 (bytes)Size of program headers:0 (bytes)Number of program headers:0Size of section headers:64 (bytes)Number of section headers:13Section header string table index: 10...$$$ readelf -a libtest.aFile: libtest.a(test.o)ELF Header:Magic:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class:ELF64Data:2's complement, little endianVersion:1 (current)OS/ABI:UNIX - System VABI Version:0Type:REL (Relocatable file)Machine:Advanced Micro Devices X86-64Version:0x1Entry point address:0x0Start of program headers:0 (bytes into file)Start of section headers:680 (bytes into file)Flags:0x0Size of this header:64 (bytes)Size of program headers:0 (bytes)Number of program headers:0Size of section headers:64 (bytes)Number of section headers:13Section header string table index: 10...动态库动态库是 ELF 文件 。假设有 test.cpp 文件,执行下面的命令将其编译为动态库 libtest.so 和静态库 libtest.a:
$ g++ -fPIC test.cpp -shared -o libtest.so$$ g++ test.cpp -c -o test.o $ ar cr libtest.a test.o然后通过 readelf 命令读取其 ELF 信息,不难发现相较于静态库 libtest.a,动态库 libtest.so 的 ELF 信息多出一些 program headers 。而我们知道,program headers 提供的信息是程序运行时需要的,这一点和动态库在程序运行时被链接相印证 。
$ readelf -a libtest.soELF Header:Magic:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class:ELF64Data:2's complement, little endianVersion:1 (current)OS/ABI:UNIX - System VABI Version:0Type:DYN (Shared object file)Machine:Advanced Micro Devices X86-64Version:0x1Entry point address:0x5a0Start of program headers:64 (bytes into file)Start of section headers:6264 (bytes into file)Flags:0x0Size of this header:64 (bytes)Size of program headers:56 (bytes)Number of program headers:7Size of section headers:64 (bytes)Number of section headers:29Section header string table index: 26...Program Headers:TypeOffsetVirtAddrPhysAddrFileSizMemSizFlagsAlignLOAD0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000754 0x0000000000000754R E200000LOAD0x0000000000000e00 0x0000000000200e00 0x0000000000200e000x0000000000000228 0x0000000000000230RW200000DYNAMIC0x0000000000000e18 0x0000000000200e18 0x0000000000200e180x00000000000001c0 0x00000000000001c0RW8NOTE0x00000000000001c8 0x00000000000001c8 0x00000000000001c80x0000000000000024 0x0000000000000024R4GNU_EH_FRAME0x00000000000006d0 0x00000000000006d0 0x00000000000006d00x000000000000001c 0x000000000000001cR4GNU_STACK0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000RW10GNU_RELRO0x0000000000000e00 0x0000000000200e00 0x0000000000200e000x0000000000000200 0x0000000000000200R1...

就 ELF 文件结构而言,动态库和可执行程序并无太多区别 。
unresolved 符号有了前文的分析,我们现在应该明白动态库和静态库关于 unresolved 符号的区别了 。在链接时,静态库中可以有 unresolved(未解决)的符号,只要程序不引用这些 unresolved 符号——不引用含有 unresolved 符号的 .o 文件里的所有符号 。
因为“静态库被传递给链接器后,链接器从静态库中提取出 .o 文件,然后从这些 .o 文件中挑选出自己需要的使用 。”
而动态库是独立的 ELF 文件,如果程序链接的是动态库,那么我们必须 resolve(解决)库里的所有的 unresolved 符号 。因此就本例而言,要链接 libpigi.so 库,我们必须同时也引用 Octtools,即使程序没有使用 Octtolls 。
动态库不能包含 unresolved 符号,意味着动态库中的所有符号都是可用的 。
符号可见性(symbols visibility)在 Linux 系统中,所有非静态的全局符号对外默认都是可见的,“默认”一词意味着有手段改变符号的可见性 。
编译器的 -fvisibility 选项编写函数或者类时,指定 __attribute__((visibility("default"))) 属性,如此一来,在编译时便可通过 -fvisibility 选项限制相应符号的可见性 。详情可参考这里 。
链接器的 --version-script 选项该选项可以为链接器指定版本控制脚本,支持动态库的 ELF 平台都可以使用,通常在创建动态库时使用,以指定所创建库的版本层次结构附加信息 。详情可参考这里 。下面是一个实例:


推荐阅读