最近某个程序需要调用多个动态库,但是不同的动态库中使用了功能不同的同名函数,导致调用出现偏差,可参考以下解决办法。另外c++也可以通过增加命名空间来解决冲突。

样例

假设有动态库libFuncA.so和libFuncB.so,他们的内部实现分别为:

/*func_A.c*/
#include<stdio.h>
//内部函数
int sayHi()
{
    printf("Hi,this is AAAAA\n");    
    return 0;
}
//外部调用函数
int sayOut()
{
    sayHi();
    printf("Use this to introduce AAAAA\n");
    return 0;    
}
123456789101112131415
/*func_B.c*/
#include<stdio.h>
//内部函数
int sayHi()
{
    printf("Hi,this is BBBBB\n");    
    return 0;
}
//外部调用函数
int sayOut()
{
    sayHi();
    printf("Use this to introduce BBBBB\n");
    return 0;    
}

分别编译成动态库:
gcc -fPIC -shared -o libFuncA.so func_A.c
gcc -fPIC -shared -o libFuncB.so func_B.c

调用函数test.c:

#include<stdio.h>
extern int sayOut();
int main()
{
    sayOut();
    return 0;
}

则调用的时候,使用函数sayOut,实现的功能与编译时链接库顺序有关。
运行结果

阶段一

可使用dlopen函数族,显式指定要调用的动态库。
详细用法如:dlopen
该函数族需设定打开模式,返回一个动态库的句柄,调用句柄和函数进行操作,完成后需要关闭。
使用时,需引入头文件 dlfcn.h,定义函数指针, 编译时增加 -rdynamic 参数和链接 -ldl

更改调用函数test.c

#include<stdio.h>
#include<dlfcn.h>

extern int sayOut();

typedef int (*func_pt)();
int main()
{
    void *handle = NULL;
    func_pt func = NULL;
    
    if((handle = dlopen("./libFuncA.so", RTLD_LAZY)) == NULL)
    {
        printf("dlopen %s\n", dlerror());    
        return -1;
    }
    //定义函数指针,在动态库中查找符号
    func = dlsym(handle, "sayOut");
    func();
    dlclose(handle);
    
    printf("+++++++++++++++++++++++++++++++++++++\n");

    if((handle = dlopen("./libFuncB.so", RTLD_LAZY)) == NULL)
    {
        printf("dlopen %s\n", dlerror());    
        return -1;
    }
    func = dlsym(handle, "sayOut");
    func();
    dlclose(handle);    
}

编译执行:
运行结果
可以发现,我们能显式执行指定动态库的外部函数sayOut了,但是内部函数sayHi的调用还是和我们编译时链接的动态库顺序有关。

进阶二

为何内部函数sayHi都调用了链接顺序第一个的实现?
原因在于动态库中的内部函数没有设置限制,使得sayHi函数也暴露给外部,调用时自然选择第一个函数实现。
用nm指令可以看出,两个函数都暴露出来了
运行结果
再次搜索,可以用gcc编译器的特性来设置动态库函数的导出控制。
可在函数前增加__attribute__ 前缀来控制
更改动态库函数如下:

/* func_A.c*/
#include<stdio.h>

#define DLL_PUBLIC __attribute__((visibility("default")))
#define DLL_LOCAL  __attribute__((visibility("hidden")))

DLL_LOCAL int sayHi()
{
    printf("Hi,this is AAAAA\n");    
    return 0;
}

DLL_PUBLIC int sayOut()
{
    sayHi();
    printf("Use this to introduce AAAAA\n");
    return 0;    
    
}

/* func_B.c*/
#include<stdio.h>

#define DLL_PUBLIC __attribute__((visibility("default")))
#define DLL_LOCAL  __attribute__((visibility("hidden")))

DLL_LOCAL int sayHi()
{
    printf("Hi,this is BBBBB\n");    
    return 0;
}

DLL_PUBLIC int sayOut()
{
    sayHi();
    printf("Use this to introduce BBBBB\n");
    return 0;    
    
}

重新编译动态库,用nm指令可以查看sayHi不再导出了
在这里插入图片描述
重新编译测试程序,则运行正常啦
在这里插入图片描述

进阶三

如果两个动态库中相似函数很多,一个个加 __attribute__前缀也是很大工作量。此时可以编译时设置默认函数不导出,只在需要导出的函数前面加前缀。以libFuncA.c为例:

/*funcA.c*/
#include<stdio.h>

#define DLL_PUBLIC __attribute__((visibility("default")))

int sayHi()
{
    printf("Hi,this is BBBBB\n");    
    return 0;
}

DLL_PUBLIC int sayOut()
{
    sayHi();
    printf("Use this to introduce BBBBB\n");
    return 0;    
    
}

编译时,增加-fvisibility=hidden 参数,则未增加前缀的函数都不会导出
在这里插入图片描述

进阶四

最近一段时间对接海康闸机嵌入式linux系统,双方遇到的问题记录总结

问题1 :json库冲突
处理方式
jsoncpp/cjson库冲突,通过增加命名空间进行处理的

问题2 :openssl链接后端失败
处理方式
1、先分析问题,执行下面指令获取后端服务采用的ssl链接方式

$ curl -v https://********/gateway.do
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs/
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / AES256-SHA

修改代码,增加下面代码

//指定加密套件
curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, "AES256-SHA");
//指定ssl链接版本
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);

2、还存在问题的话,可能就是openssl库冲突造成的
参考该文章linux c解决多个第三方so动态库包含不同版本openssl造成的符号冲突

解决办法:dlopen加载so动态库时,加了参数RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND之后,程序功能正常了,正常了!

当希望dlopen载入的库首先从自己和它的依赖库中查找符号,然后再去全局符号中去查找时,就用RTLD_DEEPBIND。这样就允许dlopen载入的库中存在与全局符号重名的符号,而对这个载入的库来说错的或者可能引起问题的全局符号可能是由其它库引入的。

问题3 :内存或是堆栈错误
处理方式
根据 gdb定位到相应函数,发现是局部变量堆栈空间分配太大导致的。建议可以改为动态分配。嵌入式系统环境资源都是比较有限的,PC正常运行,嵌入式arm linux就可能出现各种奇怪问题。

问题4 :sm3国密签名算法pc端和嵌入式端运行结果不一致
处理方式
项目时间紧张,先采用md5方式签;根据分析,可能是sm3算法涉及到的循环左移或是循环右移在嵌入式arm linux平台不支持导致的,待进一步研究了。。。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐