为何CMake会将IMPORTED库的绝对路径硬编码到二进制文件中?
问题描述
我正在使用CentOS 7、gcc10、CMake 3.20与Ninja 1.10.2,为一个不支持CMake的库实现了find_package相关配置:
add_library(MyLib UNKNOWN IMPORTED) target_include_directories(MyLib INTERFACE "$ENV{MY_LIB}/include") target_link_libraries(MyLib INTERFACE ${CMAKE_DL_LIBS}) set_property(TARGET MyLib PROPERTY IMPORTED_LOCATION "$ENV{MY_LIB}/lib/libmylib.so" )
在项目中通过以下方式引用该库:
find_package(MyLib REQUIRED PATHS "${CMAKE_MODULE_PATH}" NO_DEFAULT_PATH) target_link_libraries(myApp PUBLIC MyLib )
出现异常行为:链接器将libmylib.so的绝对路径写入myApp目标文件,导致移动整个项目后库加载失败,报错:
/data/projects/myApp/external/myLib/lib/libmylib.so: cannot open shared object file: No such file or directory
注:LD_LIBRARY_PATH已设置且有效。
更新1
执行readelf -d myApp | head -20,结果显示该库的绝对路径出现在NEEDED条目里:
[user]$ readelf -d myApp | head -20 Dynamic section at offset 0x101588 contains 73 entries: Tag Type Name/Value 0x0000000000000001 (NEEDED) Shared library: [libsth1.so] 0x0000000000000001 (NEEDED) Shared library: [libsth2.so] 0x0000000000000001 (NEEDED) Shared library: [/data/projects/myApp/external/myLib/lib/libmylib.so]
更新2
尝试设置以下变量但无效果:
set(CMAKE_SKIP_BUILD_RPATH TRUE) set(CMAKE_SKIP_RPATH TRUE)
请问为何libmylib.so的路径会被硬编码?
解决方案
原因分析
核心问题在于你在IMPORTED_LOCATION中使用了绝对路径指向libmylib.so,CMake会直接将这个完整路径传递给链接器,链接器就会把该绝对路径硬编码到可执行文件的动态依赖(NEEDED条目)中。
而CMAKE_SKIP_BUILD_RPATH和CMAKE_SKIP_RPATH仅控制是否添加RPATH路径,和NEEDED条目的内容无关,因此设置这两个变量无法解决当前问题。
解决办法
需要让CMake传递库文件名而非绝对路径给链接器,同时确保链接器能找到该库,有两种可行方式:
- 设置
IMPORTED_NO_SONAME属性
给导入库添加IMPORTED_NO_SONAME属性并设为TRUE,CMake会忽略库本身的SONAME,仅将库文件名传递给链接器:
add_library(MyLib UNKNOWN IMPORTED) target_include_directories(MyLib INTERFACE "$ENV{MY_LIB}/include") target_link_libraries(MyLib INTERFACE ${CMAKE_DL_LIBS}) set_property(TARGET MyLib PROPERTY IMPORTED_LOCATION "$ENV{MY_LIB}/lib/libmylib.so" ) # 添加该行配置 set_property(TARGET MyLib PROPERTY IMPORTED_NO_SONAME TRUE)
- 指定链接搜索路径,传递库名
不直接用绝对路径指定库,而是通过target_link_directories告诉链接器库的所在目录,之后仅传递库名即可:
add_library(MyLib UNKNOWN IMPORTED) target_include_directories(MyLib INTERFACE "$ENV{MY_LIB}/include") # 添加链接目录 target_link_directories(MyLib INTERFACE "$ENV{MY_LIB}/lib") # 仅传递库名mylib,链接器会自动匹配libmylib.so target_link_libraries(MyLib INTERFACE mylib ${CMAKE_DL_LIBS})
这种方式下,链接器会在指定目录找到libmylib.so,并在NEEDED条目中只写入libmylib.so,运行时就会通过LD_LIBRARY_PATH查找该库。
内容的提问来源于stack exchange,提问作者TonySalimi
相关产品推荐
相关产品推荐

