发布网友 发布时间:2024-09-30 03:49
共1个回答
热心网友 时间:2024-12-02 09:32
动态库中函数调用环境:
clangversion12.0.1
cmakeversion3.21.2
g++(GCC)11.1.0
包含两个相同函数的库链接冲突试想一下,在你的主函数中调用了一个函数,但是这个函数可以在不同的库中都有实现,那主函数中调用的应该是哪一个呢?
我们来试验一下,构建测试代码:
.├──CMakeLists.txt├──include│├──second.h│└──work.h├──main.cc└──src├──CMakeLists.txt├──second.cc└──work.cc./CMakeLists.txt文件内容:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)./include/second.h
#pragmaonce#include<iostream>voidSoFunction();voidDoFunction();./include/work.h
#pragmaonce#include<iostream>voidSoFunction();src/CMakeLists.txt内容:
include_directories(${CMAKE_PROJECT_PATH}/include)add_library(secondSHAREDsecond.cc)add_library(workSHAREDwork.cc)src/second.cc
#include"second.h"voidSoFunction(){std::cout<<"conflictfunctioncall\n";}voidDoFunction(){std::cout<<"DoFunction\n";SoFunction();}src/work.cc
#include"work.h"voidSoFunction(){std::cout<<"CallSofunctiuon\n";}./main.cc
#include<iostream>voidSoFunction();intmain(){std::cout<<"1.Mainstart...\n";SoFunction();std::cout<<"SoFunctioncallfinished\n";return0;}构建运行:mkdirbuild&&pushdbuild&&cmake..&&make&&./symbol&&popd&&rm-rfbuild执行结果如下:
conflictfunctioncallSoFunctioncallfinished可以看到主函数调用了second.cc里面的SoFunction,因为我们先link的时libsecond.so的动态库,自然后link的libwork.so会被覆盖,如果我们在./CMakeLists.txt中调整link顺序,你将能得到不同的结果.
包含两个相同函数的动态库调用覆盖问题如果我们在主函数中调用second/DoFunction其会调用SoFunction,那么这时会调用second.cc的SoFunction还是调用work.cc的SoFunction?修改main函数进行测试:
#include<iostream>#include"second.h"#include"work.h"intmain(){std::cout<<"1.Mainstart...\n";DoFunction();std::cout<<"DoFunctioncallfinished\n";SoFunction();return0;}这时候我们不修改link顺序,保持second先link,我们得到下面的结果:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)0不出意外,函数调用顺序为:main.cc/DoFunction->second.cc/DoFunction->second.cc/SoFunction->work.cc/SoFunction现在我们修改./CMakeLists.txt以调整link顺序target_link_libraries(${PROJECT_NAME}worksecond),重新运行代码,我们将得到下面的结果:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)1函数的调用顺序为:main.cc/DoFunction->second.cc/DoFunction->work.cc/SoFunction->work.cc/SoFunction,这时因为函数SoFunction因为libwork.so先link,函数使用其的.那如何link先后顺序发生变化的情况下仍然实现同一个库的函数相互调用?
我们先看看两个库中的符号信息:nm-CDsrc/libwork.so,内容如下:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)2libsecond.so里面的内容:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)3解决符号冲突导致的错误调用隐藏符号我们可以通过设置invisiable让动态库的符号是否对外可见,例如我们可以让libwork.so的符号对外不可见,那么我们就可以无论怎样都能调用到second的函数.你可以设置src/CMakeLists.txt的内容如下:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)4上面的编译过程会自动将两个库的符号隐藏(CXX_FLAG在当前CMakeLists.txt一直生效,不能直接设置一个加参数,一个不加),这时候我们看到libwork.so的符号信息,执行上面的编译运行命令的时候会出错,因为找不到符号表DoFunction和SoFunction,从而出现未定义的引用.
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)5这里我们使用手动link生成最后的二进制文件:clang++main.cc-Iinclude-L.-lsecond-lwork-osymbol这时候我们执行的时候将屏蔽libwork.so里面的符号,从而实现只调用second.cc里面的SoFunction.结果如下:
1.Mainstart...SecondDoFunctionconflictfunctioncallSoFunctioncallfinishedconflictfunctioncall函数代码中禁用在libsecond.so中人为开启SoFunction和DoFunction两个函数对外可见,修改second.cc(单独编译second.cc为libsecond.soclang++-fvisibility=hidden-Iincludesrc/second.cc-fPIC-shared-olibsecond.so)
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)7然后在src/CMakeLists.txt开启visibility,编译即可正常运行,因为work.cc里面的符号没有对外暴露,所以我们不会调到work.cc里面的SoFunction.
通过文件设置导出函数表在include/export.symb文件中添加下面信息:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)8global:对应的是你想导出的函数
local区域代表不想导出的符号,*号表示除了global中的符号全部不导出
手动编译动态库实现只保留SoFunctionclang++-Wl,--version-script=include/export.symb-s-Iincludesrc/second.cc-fPIC-shared-olibsecond.so,此时libsecond.so的内容如下:
cmake_minimum_required(VERSION3.14)project(symbol)include_directories(include)add_subdirectory(src)add_executable(${PROJECT_NAME}main.cc)target_link_libraries(${PROJECT_NAME}secondwork)9输出如下:
#pragmaonce#include<iostream>voidSoFunction();voidDoFunction();0这里的输出有点不一样(我们屏蔽了second.cc里面的SoFunction)
参考部分内容参考了:编译链接时如何解决符号冲突问题