我有一个共享库,我用它来创建一个可执行的二进制文件。我只能控制库的构建过程和可执行的二进制文件,而不是所涉及的源文件。正如所料,可执行二进制文件中的源文件引用了库中的许多函数。
目前,共享库是直接使用objectfiles(.o)构建的。
g++ -shared ${OBJECT_FILES} -o ${SHARED_LIBRARY}
我想通过将它们分组到静态库(.a文件或achive)中来发布目标文件。
为了节省空间,我在创建存档文件时删除了所有.o文件。所以现在,构建命令是
g++ -shared ${ACRCHIVE_FILE} -o ${SHARED_LIBRARY}
图书馆建设很好
但是,当我尝试通过链接到此共享库来构建可执行二进制文件时,未定义二进制文件所指示的符号,并且无法链接。 (未定义的Context::Get()
引用)
根据我的理解,如果我们直接使用.o文件创建共享库或者包含所有.o文件的存档,这应该无关紧要,但显然要么不可能,要么我可能会遗漏某些内容。
答案 0 :(得分:2)
共享库libfoo.so
应包含PIC代码,静态库libfoo.a
包含普通的非PIC代码。因此,您无法从静态库创建共享库。
共享库需要位置无关代码,因为它们的段mmap(2) - 几乎是任意和可变的地址 - 没有MAP_FIXED
...还ASLR。
原则上你可以从PIC目标文件构建一个静态库,但实际上没有人这样做。
你可能(如果你真的坚持)制作一个由非PIC代码组成的ELF共享对象,但结果会有非常糟糕的表现;动态链接器会有很多relocations,因此大多数段都不会被共享,动态链接会非常慢。
将共享库的foo.cc
编译为PIC对象文件foo.pic.o
:
g++ -Wall -c -O foo.cc -fPIC -o foo.pic.o
为静态库编译一个普通的非PIC对象文件foo.o
:
g++ -Wall -c -O foo.cc -o foo.o
在foo.pic.o
个共享库中将bar.pic.o
和libfoobar.so
的共享库转换为libdep.so
个链接:
g++ -shared foo.pic.o bar.pic.o -ldep -o libfoobar.so
在制作上述共享库时,您经常需要添加更多链接选项,例如-Wl,-rpath,
...或-Wl,-soname,
....
BTW,无法将归档libfoobar.a
(即使它由PIC文件组成)链接到libfoobar.so
,因为没有名称未定义,并且需要链接来自{{1的一些目标文件(或许您可以尝试使用libfoobar.a
取消定义某个符号symb
,但我不建议这样做)。
将-u symb
和foo.o
的静态库转换为bar.o
:
libfoobar.a
请注意,ranlib(创建归档的索引)不再需要,因为GNU ar cv libfoobar.a foo.o bar.o
完成了它的工作。
另请阅读ld.so(8),ldd(1),ld(1),ar(1),objdump(1),readelf(1),dlopen(3)(通常,您如果主程序在运行时加载ar
- ed插件,则需要将主程序与-rdynamic
链接,以使插件能够在主程序中找到一些符号。
注意:我只是构建一个共享库而不关心静态链接。请注意,在某些系统上,PIC的成本略高(代码稍大和/或较慢)。 AFAIK,PIC开销在x86-64上的成本低于32位Linux x86代码。在一些架构和ABI上,PIC可能具有可忽略的开销(或者甚至可能比非位置无关代码更有效)。
参考文献:Drepper's paper: How to Write Shared Libraries& Levine's book: Linkers and Loaders& Program Library HowTo