该文章使用这一章的测试工程。
发现问题
为了部署方便,现让所有的模块生成静态库(.a)。即, libhello-world.so ---> libhello-world.a, libhello.so ---> libhello.a, libworld.so ---> libworld.a。(静态库和动态库的优缺点)
如果将上述三个动态库同时改为静态库是没问题的,编译、执行,很顺利,目的已达到。但是,在测试过程中,有一个场景会产生问题。
场景如下(注意 libhello):
├────libhello.a
可执行文件────libhello-world.so
├────libworld.so
在只将 libhello.so 改为 libhello.a,而 libhello-world.so 为动态库时,编译过程中产生如下错误信息:
Linking C shared library libhello-world.so
/usr/bin/ld: hello/libhello.a(hello.c.o): relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
hello/libhello.a: 无法添加符号: 错误的值
collect2: error: ld returned 1 exit status
hello-world/CMakeFiles/hello-world.dir/build.make:85: recipe for target 'hello-world/libhello-world.so' failed
make[2]: *** [hello-world/libhello-world.so] Error 1
CMakeFiles/Makefile2:133: recipe for target 'hello-world/CMakeFiles/hello-world.dir/all' failed
make[1]: *** [hello-world/CMakeFiles/hello-world.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
产生的原因很明确:can not be used when making a shared object——在生成动态库(libhello-world.so)的过程中产生的错误。解决方法是:recompile with -fPIC。什么意思?"-fPIC" 是什么东西?应该把它放在哪?
说明
PIC:position independent code,地址无关码。
生成动态库时必须使用地址无关码。因为在动态库被装载时,其地址是不确定的。因此在用静态库生成动态库时需要将静态库“转换”为地址无关码。
-fPIC:gcc 编译源文件生成目标文件时通过该参数使生成的目标文件为地址无关码。
cmake 生成地址无关码
以下为cmake生成地址无关码的几种方式:
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
add_compile_options(-fPIC)
target_compile_options(${PROJECT_NAME} PRIVATE -fPIC)
set_property(TARGET lib1 PROPERTY POSITION_INDEPENDENT_CODE ON)
在这里,我推荐最后两种方式:target_compile_options()
和 set_property()
。因为这两个命令都指明了目标文件,不会对其他目标文件有影响。
注:cmake 生成动态库(.so)时会自动指定 -fPIC
。
参考
https://stackoverflow.com/questions/38296756/what-is-the-idiomatic-way-in-cmake-to-add-the-fpic-compiler-option
https://stackoverflow.com/questions/19364969/compilation-fails-with-relocation-r-x86-64-32-against-rodata-str1-8-can-not