解决动态库链接过程中的符号冲突问题
动态库中函数调用
环境:
clang version 12.0.1
cmake version 3.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(VERSION 3.14) project(symbol) include_directories(include) add_subdirectory(src) add_executable(${PROJECT_NAME} main.cc) target_link_libraries(${PROJECT_NAME} second work) 复制代码
./include/second.h
#pragma once #include <iostream> void SoFunction(); void DoFunction(); 复制代码
./include/work.h
#pragma once #include <iostream> void SoFunction(); 复制代码
src/CMakeLists.txt
内容:
include_directories(${CMAKE_PROJECT_PATH}/include) add_library(second SHARED second.cc) add_library(work SHARED work.cc) 复制代码
src/second.cc
#include "second.h" void SoFunction() { std::cout << "conflict function call\n"; } void DoFunction() { std::cout<<"DoFunction\n"; SoFunction(); } 复制代码
src/work.cc
#include "work.h" void SoFunction() { std::cout << "Call Sofunctiuon\n"; } 复制代码
./main.cc
#include <iostream> void SoFunction(); int main() { std::cout<<"1. Main start... \n"; SoFunction(); std::cout<<"SoFunction call finished\n"; return 0; } 复制代码
构建运行:mkdir build && pushd build && cmake .. && make && ./symbol && popd && rm -rf build
执行结果如下:
conflict function call SoFunction call finished 复制代码
可以看到主函数调用了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" int main() { std::cout<<"1. Main start... \n"; DoFunction(); std::cout<<"DoFunction call finished\n"; SoFunction(); return 0; } 复制代码
这时候我们不修改link顺序,保持second先link,我们得到下面的结果:
1. Main start... Second DoFunction conflict function call DoFunction call finished conflict function call 复制代码
不出意外,函数调用顺序为:main.cc/DoFunction->second.cc/DoFunction->second.cc/SoFunction->work.cc/SoFunction
现在我们修改./CMakeLists.txt
以调整link顺序target_link_libraries(${PROJECT_NAME} work second)
,重新运行代码,我们将得到下面的结果:
1. Main start... Second DoFunction Call Sofunctiuon DoFunction call finished Call Sofunctiuon 复制代码
函数的调用顺序为:main.cc/DoFunction->second.cc/DoFunction->work.cc/SoFunction->work.cc/SoFunction
,这时因为函数SoFunction
因为libwork.so
先link,函数使用其的.那如何link先后顺序发生变化的情况下仍然实现同一个库的函数相互调用?
我们先看看两个库中的符号信息:nm -CD src/libwork.so
,内容如下:
U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000001129 T SoFunction() U std::ios_base::Init::Init()@GLIBCXX_3.4 U std::ios_base::Init::~Init()@GLIBCXX_3.4 U std::cout@GLIBCXX_3.4 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<c har> >&, char const*)@GLIBCXX_3.4 复制代码
libsecond.so里面的内容:
U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000001159 T DoFunction() 0000000000001139 T SoFunction() U std::ios_base::Init::Init()@GLIBCXX_3.4 U std::ios_base::Init::~Init()@GLIBCXX_3.4 U std::cout@GLIBCXX_3.4 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<c har> >&, char const*)@GLIBCXX_3.4 复制代码
解决符号冲突导致的错误调用
隐藏符号
我们可以通过设置invisiable让动态库的符号是否对外可见,例如我们可以让libwork.so
的符号对外不可见,那么我们就可以无论怎样都能调用到second的函数.你可以设置src/CMakeLists.txt
的内容如下:
include_directories(${CMAKE_PROJECT_PATH}/include) if(UNIX AND CMAKE_COMPILER_IS_GNUCC) set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") add_library(work SHARED work.cc) endif() add_library(second SHARED second.cc) 复制代码
上面的编译过程会自动将两个库的符号隐藏(CXX_FLAG在当前CMakeLists.txt一直生效,不能直接设置一个加参数,一个不加),这时候我们看到libwork.so的符号信息,执行上面的编译运行命令的时候会出错,因为找不到符号表DoFunction
和SoFunction
,从而出现未定义的引用.
U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable U std::ios_base::Init::Init()@GLIBCXX_3.4 U std::ios_base::Init::~Init()@GLIBCXX_3.4 U std::cout@GLIBCXX_3.4 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@GLIBCXX_3.4 复制代码
这里我们使用手动link生成最后的二进制文件:clang++ main.cc -Iinclude -L. -lsecond -lwork -o symbol
这时候我们执行的时候将屏蔽libwork.so
里面的符号,从而实现只调用second.cc
里面的SoFunction
.结果如下:
1. Main start... Second DoFunction conflict function call SoFunction call finished conflict function call 复制代码
函数代码中禁用
在libsecond.so
中人为开启SoFunction
和DoFunction
两个函数对外可见,修改second.cc
(单独编译second.cc为libsecond.soclang++ -fvisibility=hidden -Iinclude src/second.cc -fPIC -shared -o libsecond.so
)
#include "second.h" __attribute__ ((visibility ("default"))) void SoFunction() { std::cout << "conflict function call\n"; } __attribute__ ((visibility ("default"))) void DoFunction() { std::cout<<"Second DoFunction\n"; SoFunction(); } 复制代码
然后在src/CMakeLists.txt
开启visibility,编译即可正常运行,因为work.cc
里面的符号没有对外暴露,所以我们不会调到work.cc里面的SoFunction
.
通过文件设置导出函数表
在include/export.symb文件中添加下面信息:
{ global: *SoFunction*; local: *; }; 复制代码
global:对应的是你想导出的函数
local区域代表不想导出的符号,*号表示除了global中的符号全部不导出
手动编译动态库实现只保留SoFunctionclang++ -Wl,--version-script=include/export.symb -s -Iinclude src/second.cc -fPIC -shared -o libsecond.so
,此时libsecond.so的内容如下:
U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000001190 T DoFunction() U std::ios_base::Init::Init()@GLIBCXX_3.4 U std::ios_base::Init::~Init()@GLIBCXX_3.4 U std::cout@GLIBCXX_3.4 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<c har> >&, char const*)@GLIBCXX_3.4 复制代码
输出如下:
1. Main start... Second DoFunction conflict function call DoFunction call finished Call Sofunctiuon 复制代码
这里的输出有点不一样(我们屏蔽了second.cc里面的SoFunction)
作者:bleedingfight
链接:https://juejin.cn/post/7010421599572590629
伪原创工具 SEO网站优化 https://www.237it.com/