添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

最近某个程序需要调用多个动态库,但是不同的动态库中使用了功能不同的同名函数,导致调用出现偏差,可参考以下解决办法。 另外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平台不支持导致的,待进一步研究了。。。

最近某个程序需要调用多个动态库,但是不同的动态库中使用了功能不同的同名函数,导致调用出现偏差,可参考以下解决办法。样例假设有动态库libFuncA.so和libFuncB.so,他们的内部实现分别为:/*func_A.c*/#include&lt;stdio.h&gt;//内部函数int sayHi(){ printf("Hi,this is AAAAA\n"); return 0;}//外部调用函数int sayOut(){ sayHi();
在C编程中,可能引用的多个外源库应用了相同的函数名,例如crypto++和openssl都有SHA1函数,用于做SHA1的hash计算,但两个函数用法不同,会出现冲突 比如已经使用了using namespace CryptoPP; 这时默认使用了Crypto++的库函数 如果调用openssl的SHA1函数,则会显示出错 解决方法是使用“::SHA1(函数参数);” ::的意思是回到库
在使用多个动态库时,两个动态库之间有可能存在相同名称的函数,这样会出现只有第一个函数生效,即所有对该函数的调用都将指向第一个加载的动态库同名函数中。这样就会很混乱,而且在想改名称也不是很简单的情况下就会很麻烦(例如同名函数太多,甚至就是模板)等等。 处理方法 这里只找到一种处理方法,当然你直接把名字改成不一样或者统一添加前缀(例如C++ namespace)也不错。方法如下: 变量会通过数据段的GOT表来访问。 而且,当可执行文件中(由于可执行文件不是PIC的)有同名全局变量时,共享库中定义的变量会被ignore,进程中只会存在可执行程序中的super_a。
1、最近同事遇到了一个程序崩溃的问题,后来找到原因,是因为这个程序引用了多个动态库,而其中两个动态库中有一个类重名了! 难道gcc对符号重名不做检测的吗?自己觉得有趣,就做了个测试: //m1.cpp #include int get_value() return 1; int get_m1() return get_value(); //m2.cpp 由于两个opencv的dll名字不同,所以它们不会覆盖,而是共存(但是它们的头文件都是一样的)。这样带来一种假象,就是dll1和dll2将各自调用各自的opencv库。但事实并非如此。实际上,在链接时,先被链接的dll的函数将覆盖后面的同名函数。 C/C++控制台串口通讯编程是一种通过使用C/C++语言来实现与串口设备进行通信的编程技术。 在控制台串口通讯编程中,我们可以使用C/C++语言提供的相关库函数来访问和操作串口设备。其中,包括了打开串口、设置串口属性、读写串口数据等功能。 首先,我们需要使用C/C++的库函数来打开串口设备。通过指定串口的名称和访问权限,我们可以从操作系统中获得对串口设备的访问句柄。然后,我们可以使用该句柄来设置串口的属性,如波特率、数据位数、停止位等。设置好属性后,我们就可以通过该串口进行数据的收发。 在串口通讯中,数据的读写是通过调用读写函数来实现的。通常,我们可以使用读函数来从串口中读取数据,并将其存储到缓冲区中。而写函数则用于将数据从缓冲区中写入到串口中。通过读写函数的调用,我们可以实现与外部设备之间的数据交互。 此外,对于串口通讯编程,还需要注意处理错误和异常情况。在进行串口操作时,可能会出现一些错误,如无法打开串口、读写超时等。为了确保程序的可靠性,我们需要在代码中添加适当的错误处理机制,以便及时发现和处理异常情况。 总而言之,C/C++控制台串口通讯编程是一种实现与串口设备进行通信的编程技术。通过使用相关的库函数,我们可以打开串口、设置属性、读写数据,并处理可能出现的错误情况,以实现与外部设备的数据交互。 ### 回答2: C/C++ 控制台串口通讯编程是一种通过串口与外部设备进行数据通讯的编程技术。在控制台应用程序中,通过使用C/C++ 编程语言来实现与串口通讯的功能。 首先,需要了解操作系统提供的相关串口通讯 API 接口。在Windows系统中,可以使用Windows API函数来访问串口,如CreateFile()、ReadFile() 和 WriteFile() 等函数。在Linux系统中,可以通过打开文件描述符的方式来访问串口设备文件,并使用read() 和 write() 函数进行数据的读写。 其次,需要设置串口的参数,包括波特率、数据位、停止位、校验位等。这些参数需要根据外部设备的通讯规范进行设置,以确保正确的数据传输。 然后,可以通过编写串口数据发送和接收的函数来实现数据的收发。发送数据时,可以使用WriteFile() 或 write() 函数将数据写入串口缓冲区,并等待数据的发送完成。接收数据时,可以使用ReadFile() 或 read() 函数从串口缓冲区中读取数据。 此外,需要注意在编程过程中处理异常情况。例如,当串口无法打开、写入数据超时或读取数据错误时,应设置相应的错误处理机制,例如打印错误信息或重新尝试等。 最后,可以在控制台应用程序中实现用户交互界面,通过命令行参数或菜单选项来控制串口通讯的功能,例如设置参数、发送数据、接收数据等。 总之,C/C++ 控制台串口通讯编程需要理解串口通讯的原理和外部设备的通讯规范,熟悉操作系统提供的串口访问函数,并编写相应的发送和接收函数来实现数据的传输。 ### 回答3: C/C++控制台串口通讯编程是指使用C/C++编程语言,在控制台环境中通过串口与外部设备进行通讯的编程过程。 串口通讯是一种常见的硬件通讯接口,用于计算机与外围设备的数据传输。C/C++是一种常见的程序设计语言,提供了丰富的库函数和语法特性,可以方便地进行串口通讯编程。 在进行C/C++控制台串口通讯编程时,首先需要引入相关的库文件,如Windows.h或者Linux的unistd.h header文件,这些文件包含了一些API函数,用于读取和写入串口数据。然后,通过打开相应的串口端口,设置通讯参数(如波特率、数据位、停止位等),可以使用相关API函数进行数据的发送和接收。 例如,通过使用C/C++的读取或写入文件的API函数来读取或写入串口数据,可以实现串口的数据发送和接收操作。可以通过`CreateFile()`函数来打开串口设备,通过`ReadFile()`和`WriteFile()`函数来读取和写入数据。 在进行C/C++串口通讯编程时,需要注意一些细节,比如在读取数据时需要保证数据的完整性,可以使用缓冲区来存储接收到的数据。另外,还需考虑相关的错误处理和异常情况,以确保程序的可靠性。 总之,C/C++控制台串口通讯编程是一种利用C/C++编程语言,在控制台环境下通过串口与外部设备进行数据传输的编程过程。通过合理使用相关的API函数和语言特性,可以实现串口通讯的功能,满足不同场景对数据传输的需求。