栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Linux程序cmake编译链接动态库版本号的问题

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux程序cmake编译链接动态库版本号的问题

更新:通过建立不带版本号的软链接来找到指定的库文件。

之前遇到了链接时找不到动态链接库的问题,这里找到了答案,记录一下。

原因是:代码生成的二进制文件需要动态链接libsycl.so.5 这个动态链接库。libsycl.so.5.4.0-0链接库起名为libsycl.so.5 ,当需要链接这个库文件时,回去找libsycl.so.5这个链接库的名,而不是libsycl.so了。这里原先写了找libsycl.so库,但是实际二进制代码找的是libsycl.so.5的库名,所以会有ldd 库找不到。最终通过修改find_library()可以找到了对应的库名。

 

 

为了保险起见,这里可以在find_libraries中设置多个库名

上面找寻库文件是通过find_library()函数在指定位置找到库文件后,记录在一个变量MYMATH,然后再target_link_librairies()使用实际的链接库文件。

这里贴出上述方式的完整CMakeLists.txt以便对比查看。

cmake_minimum_required(VERSION 2.8.12)


set(CMAKE_C_COMPILER "/home/wzy/sycl_workspace/build-cuda-debug/bin/clang")
set(CMAKE_CXX_COMPILER "/home/wzy/sycl_workspace/build-cuda-debug/bin/clang++")
set(CMAKE_CXX_STANDARD 17)

project(simple-sycl-app)

set(DPCPP_HOME "/home/wzy/sycl_workspace")
set(DPCPP_SYCL_HOME "${DPCPP_HOME}/build-cuda-debug")


message(STATUS "dpcpp_home : "${DPCPP_HOME})
message(STATUS "dpcpp_cuda_sycl_home : "${DPCPP_SYCL_HOME})

#include_directories("${DPCPP_SYCL_HOME}/include")
#include_directories("${DPCPP_SYCL_HOME}/include/sycl")

#target_include_directories("${DPCPP_SYCL_HOME}/build-cuda-debug/lib")
#link_directories("${DPCPP_SYCL_HOME}/build-cuda-debug/lib")
message(STATUS "find library path : ${DPCPP_SYCL_HOME}/lib")
set(CMAKE_BUILD_RPATH "${DPCPP_SYCL_HOME}/lib;${CMAKE_BUILD_RPATH}")
message(STATUS "cmake build rpath : ${CMAKE_BUILD_RPATH}")


find_library(SYCL 
    NAMES sycl libsycl.so.5
    PATHS "${DPCPP_SYCL_HOME}/lib")
message(STATUS "find sycl library is : ${SYCL}")

IF (NOT SYCL)
  message(FATAL_ERROR "libsycl.so not found")
ENDIF(NOT SYCL)

# set(CMAKE_BUILD_PATH "${DPCPP_SYCL_HOME}/lib:${CMAKE_BUILD_PATH}")

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -std=c++17")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -std=c++17")

add_compile_options(-fsycl -fsycl-targets=nvptx64-nvidia-cuda)

aux_source_directory(. DIR_SRCS)
add_executable(simple-sycl-app ${DIR_SRCS})
target_include_directories(simple-sycl-app PUBLIC "${DPCPP_SYCL_HOME}/include/sycl")
target_include_directories(simple-sycl-app PUBLIC "${DPCPP_SYCL_HOME}/include")
#target_link_libraries(simple-sycl-app -fsycl -fsycl-targets=nvptx64-nvidia-cuda -lsycl)
target_link_libraries(simple-sycl-app PRIVATE ${SYCL} )
# target_link_libraries(simple-sycl-app PRIVATE libsycl.so.5 )

更为规范便捷的做法应该是,使用link_directories()指定链接库搜索的路径,然后再target_link_libriaries()直接使用去掉前缀和后缀的库名。(这里有个前提就是存在libsycl.so的软链接指向了真正的链接库,并且链接库名的软链接在链接库搜索路径中可以找到)恰好sycl的库符合这一条件。

关于link_directories()和rpath的内容 详细讲述可以看。

传送门:cmake的rpath设置二进制文件执行时搜索路径_笑傲江湖的小白的博客-CSDN博客

这里补充一些前置知识。

Linux上对动态库的命名採用libxxx.so.a.b.c的格式。当中a代表大版本号号。b代表小版本号号,c代表更小的版本号号。我们以Linux自带的cp程序为例,通过ldd查看其依赖的动态库,例如下面,这里可以看到simple-sycl-app链接到的库文件为libsycl.so.5,其中libsycl.so.5是动态库的名字

 那么动态库的名字是怎么设定的呢?一般我们使用的时候都不会太关心版本号,链接库升级后替换动态链接库后就可以像以前一样正常使用,如果需要指定类似libsycl.so.5那也太不友好了。

动态库编译

这里的代码还是选用之前讲过的例子mymath库,这里不赘述了。详细内容可以去看

传送门:一次性弄懂gcc编译、链接、动态链接、静态链接_笑傲江湖的小白的博客-CSDN博客

采用如下命令

​gcc add.cc sub.cc -I$HOME/cpp-code/cmake-examples/01-basic/O-demo/include  -fPIC -shared -Wl,-soname,libmath.so.0 -o libmath.so.0.0.1

 设定头文件搜索路径也可以使用相对路径,我的头文件放在include目录下

gcc add.cc sub.cc -I../include  -fPIC -shared -Wl,-soname,libmymath.so.0 -o libmymath.so.0.0.1

效果如下:

需要注意的參数是-Wl,soname(中间没有空格),-Wl选项告诉编译器将后面的參数传递给链接器,
-soname则指定了动态库的soname(简单共享名。Short for shared object name)

如今我们生成了libmymath.so.0.0.1,当我们执行ldconfig -n .命令时,当前文件夹会多一个软连接libmath.so.0 就是我们动态链接指定的soname名

ldconfig -n .

 效果如下:

 这里我们也可以看一下libmymath.so.0.0.1动态库的内容

readelf -d libmymath.so.0.0.1

这里可以看到libmymath.so.0.0.1它的soname为 libmymath.so.0

这样就建立了libmath.so.0(soname软链接)到libmath.so.0.0.1(实际库文件)的软链接。

但是我们为了使用方便一般不会写库的版本名的,所以这里再创建一个软链接libmymath.so

ln -sf libmymath.so.0.0.1 libmymath.so

生成了libmymath.so软链文件,这样在程序链接时就可以直接使用链接mymath名了

事实上在生成二进制可执行文件的过程有例如以下几步

  • 链接器通过编译命令-L. -lmymath在当前文件夹查找libmymath.so文件
  • 读取libmymath.so链接指向的实际文件。这里是libmymath.so.0.0.1
  • 读取libmymath.so.0.0.1中的SONAME,这里是libmymath.so.0
  • 将libmymath.so.0记录到main程序的二进制数据里
  • 然后main程序执行时会去找soname名的库文件,这里再次找到libmymath.so.0
  • 而libmymath.so.0是实际链接到libmymath.so.0.0.1,然后链接执行。

也就是说libmymath.so.0是已经存储到可执行的二进制文件数据里的,无论这个程序在哪里,通过ldd查看它依赖的动态库都是libhello.so.0

这里通过一个链接libmymath.so的例子来说明。

例子结构:

 main.cpp

#include "shared/Hello.h"
#include 
#include "test.h"

using namespace std;

int main(int argc, char *argv[])
{
    Hello hi;
    hi.print();
    int m,n;
    cout<<"input two numbers"<>m>>n;
    cout< 

Hello.cpp

#include 

#include "shared/Hello.h"

void Hello::print()
{
    std::cout << "Hello Shared Library!" << std::endl;
}

Hello.h

#ifndef __HELLO_H__
#define __HELLO_H__

class Hello
{
public:
    void print();
};

#endif

CMakeLists.txt文件

cmake_minimum_required(VERSION 2.8.12)

set(CMAKE_C_COMPILER "/home/wzy/app/gcc-9.5.0-build/bin/gcc")
set(CMAKE_CXX_COMPILER "/home/wzy/app/gcc-9.5.0-build/bin/g++")
set(CMAKE_CXX_STANDARD 17)

project(hello_library)

############################################################
# Create a library
############################################################

#Generate the shared library from the library sources
add_library(hello_library SHARED 
    src/Hello.cpp
)
add_library(hello::library ALIAS hello_library)

target_include_directories(hello_library
    PUBLIC 
        ${PROJECT_SOURCE_DIR}/include
)


#set rpath 

# set(CMAKE_BUILD_RPATH "/home/wzy/cpp-code/cmake-examples/01-basic/O-demo/src:${CMAKE_BUILD_RPATH}")
# set(CMAKE_INSTALL_RPATH "/home/wzy/cpp-code/cmake-examples/01-basic/O-demo/src:${CMAKE_INSTALL_RPATH}")

set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

message(STATUS "cmake build rpath : ${CMAKE_BUILD_RPATH}")

#set link search path
link_directories("/home/wzy/cpp-code/cmake-examples/01-basic/O-demo/src")
############################################################
# Create an executable
############################################################

# Add an executable with the above sources
add_executable(hello_binary
    src/main.cpp
)

target_include_directories(hello_binary
    PUBLIC 
    "/home/wzy/cpp-code/cmake-examples/01-basic/O-demo/include"
)

# 打印指定的头文件搜索路径
get_property(public_private_dirs TARGET hello_binary PROPERTY INCLUDE_DIRECTORIES)
message(STATUS "public_private_dirs: ${public_private_dirs}")

set(CMAKE_BUILD_RPATH "/home/wzy/cpp-code/cmake-examples/01-basic/O-demo/src;${CMAKE_BUILD_RPATH}")
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)

message(STATUS "cmake build rpath : ${CMAKE_BUILD_RPATH}")

# link the new hello_library target with the hello_binary target
target_link_libraries( hello_binary
    PRIVATE 
        hello::library
        mymath
)

这里从CMakeLists.txt中可以看到链接的库一个是项目内的hello_library(别名为hello::library),一个是外部的mymath,这里mymath并没有指定动态库名的版本号。

执行构建和编译,打印详细日志:

mkdir build && cd build

cmake ..

#为了详细看到编译日志
make VERBOSE=1

 然后我们执行下看效果:


 

 这里我们看下生成的二进制文件,可以看到它链接的soname为libmymath.so.0是写进二进制文件的,其中-Wl,-rpath就是为了将二进制执行时搜索路径写进二进制文件中(当然这里也可以通过设置环境变量LD_LIBRARY_PATH来设定,但是这种方式会污染环境一般不这么用)

我们也可以详细看下hello_binary二进制文件。这里可以详细看到runpath跟shared library文件

所以关键就是这个soname,它相当于一个中间者。当我们的动态库仅仅是升级一个小版本号时,我们能够让它的soname相同。而可执行程序仅仅认soname指定的动态库,这样依赖这个动态库的可执行程序不需又一次编译就能使用新版动态库的特性

 动态库版本号更新

假设动态库须要做一个小小的修改,例如以下

#include "test.h"
#include 
int add(int a,int b)
{
    std::cout<<" add version 0.0.2 "< 

由于修改较小,我们编译动态库时仍然指定相同的soname

gcc add.cc sub.cc -I../include  -fPIC -shared -Wl,-soname,libmymath.so.0 -o libmymath.so.0.0.2

此时执行文件夹结构例如以下

此时文件夹下有两个版本号的动态库。但libhello.so.0指向的是老本版。移除libmymath.so.0后重新执行ldconfig -n .后我们发现,链接指向了新版本号。

重新连接libmymath.so指向新的版本

然后回到 当时编译链接好的build文件夹下 执行程序 :

 发现结果已经变化了,在没有重新编译的情况下用上了新版本的动态链接库。

但是假如有大版本的修改,编译动态链接库是指定了新的soname。例如:

gcc add.cc sub.cc -I../include  -fPIC -shared -Wl,-soname,libmymath.so.1 -o libmymath.so.1.0.0

那么源程序由于指定的是libmymath.so.0的库文件,所以就不能使用了,需要重新编译才行。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/987645.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号