linux下加载动态库函数、设置链接选项 -ldl

-ld

如果你的程序中使用dlopen、dlsym、dlclose、dlerror 显示加载动态库,需要设置链接选项 -ldl 加载动态链接库,首先为共享库分配物理内存,然后在进程对应的页表项中建立虚拟页和物理页面之间的映射。你可以认为系统中存在一种引用计数机制, 每当一个进程加载了共享库(在该进程的页表中进行一次映射),引用计数加1;一个进程显式卸载(通过dlclose等)共享库或进程退出时,引用计数减1,当减少到0时,系统卸载共享库。

gcc -o main main.c -ldl

1.dlopen

头文件:	#include <dlfcn.h>

函数定义:void *dlopen(const char* pathName, int mode);

功能:	打开指定名字(pathname)的动态链接库,并返回操作句柄。

参数:	pathname:指的是db文件或listDB.so文件在实机环境中的位置
        mode:指的是打开数据库的模式,mode在linux下,按功能有以下几种

		解析方式:
		RTLD_LAZY:暂缓决定,在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)
		RTLD_NOW:立即决定,在dlopen返回前,解析出所有未定义的符号,如果解析不出来,在dlopen会返回NULL,错误为 undefined symbol:XXX...
		
		作用范围:
		RTLD_GLOBAL: 动态库中定义的符号可被其后打开的其他库重定位
		RTLD_LOCAL: 与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其他库重定位。如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,那么默认是RTLD_LOCAL。
		
		作用方式:
		RTLD_NODELETE:dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量。这个flag不是POSIX-2001标准。
		RTLD_NOLOAD: 不加载库,可用于测试库是否已经加载(dlopen()返回NULL说明未加载,否则说明加载),也可用于改变已加载库的flag,
		如:先前加载库的flag为RTLD_LOCAL,dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL.这个flag不是POSIX-2001标准
		RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突,这个flag不是POSIX-2001标准

返回值:
        返回值void*,如果成功则返回引用这个数据库的句柄,如果失败则返回NULL

2.dlsym

头文件:	#include <dlfcn.h>

函数定义:void *dlsym(void *handle, const char *symbol);

功能:	根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址。

参数:	
		handle:由dlopen打开动态链接库后返回的指针;
		symbol:要求获取的函数或全局变量的名称。

返回值:
        void* 指向函数的地址,供调用使用。

3.dlclose

头文件:	#include <dlfcn.h>

函数定义:int dlclose (void *handle)

功能:	dlclose 用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0,才会真正被系统卸载。

参数:	
		handle:由dlopen打开动态链接库后返回的指针。

4.dlerror

头文件:	#include <dlfcn.h>

函数定义:const char* dlerror(void);

功能:	当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
代码举例说明:
#include <stdio.h>
#include <dlfcn.h>  // 包含动态链接库相关的头文件

typedef unsigned long (*PFN_SKF_ConnectDev)(char*, void*);  //连接设备

int main() 
{
    void *handle;           // 动态链接库句柄
    void *hDev = NULL;		//设备句柄
    PFN_SKF_ConnectDev pfnSKF_ConnectDev;  // 函数指针

    // 打开动态链接库
    handle = dlopen("/home/test/libSKFInterface.so", RTLD_LAZY);
    if (!handle) 
	{
        fprintf(stderr, "Error: %s\n", dlerror());
        return 1;
    }

    // 获取动态链接库中的函数指针
    pfnSKF_ConnectDev = (PFN_SKF_ConnectDev)dlsym(handle, "SKF_ConnectDev");
    if (!pfnSKF_ConnectDev) 
	{
        fprintf(stderr, "Error: %s\n", dlerror());
        dlclose(handle);
        return 1;
    }

	unsigned long result = pfnSKF_ConnectDev("devName", &hDev);
	if (0 == result)
	{
		printf("Connect successful. \n");
	}
	else
	{
		printf("Connect failed.  Error code: %lx \n",  result);
	}

    // 关闭动态链接库
    dlclose(handle);

    return 0;
}

错误信息展示:

//如果在该路径下没找到libSKFInterface.so
Error: /home/test/libSKFInterface.so: cannot open shared object file: No such file or directory
//如果libSKFInterface.so 中没找到SKF_ConnectDev
Error: /home/test/libSKFInterface.so: undefined symbol: SKF_ConnectDev

如果.so在当前路径,但是dlopen无法从你写的路径获取到.so, 用readlink读取/proc/self/exe可以获取当前程序的绝对路径。(/proc/self/exe 是一个符号链接,指向当前进程的可执行文件)

6.readlink

头文件:	#include <dlfcn.h>

函数定义:ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

功能:	读取符号链接 path 指向的内容到缓冲区 buf 中。
		

参数:	
		pathname: 要读取的符号链接的路径名。
		buf: 用于存放符号链接目标或符号链接本身的缓冲区。
		bufsiz: 缓冲区的大小,即能够存放的最大字节数。

返回:	放入 buf 的字节数。
		错误时返回 -1
代码举例说明:
#include <stdio.h>
#include <dlfcn.h>  // 包含动态链接库相关的头文件
#include <unistd.h>
#include <string.h>

char *getSoPath(char *buf, int count)
{
	int rslt = readlink((char*)"/proc/self/exe", buf, count - 1);
	if (rslt < 0 || (rslt >= count - 1))
	{
		return NULL;
	}
	buf[rslt] = '\0';
	printf("LINE = %d, buf = %s\n",__LINE__, buf);
	return buf;
}

int main() 
{
    void *handle;     // 动态链接库句柄
	char *p = NULL;
	char soPath[128] = { 0 };
	memset(soPath, 0x00, sizeof(soPath));
	getSoPath(soPath, sizeof(soPath));
	p = strrchr(soPath, '/');
	if(p != NULL)
	{
		strcpy(p + 1, "libSKFInterface.so");
	}
	printf("LINE = %d, soPath = %s\n",__LINE__, soPath);

    //打开动态链接库
    handle = dlopen(soPath, RTLD_LAZY);
	if (!handle)
	{
		fprintf(stderr, "Error: %s\n", dlerror());
		return 1;
	}
	
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 环境下,`dladdr` 函数可以用来获取某个函数或变量所在的动态库的信息,包括动态库的绝对路径。但是有时候我们会发现,`dladdr` 函数返回的 `Dl_info` 结构体中的 `dli_fname` 成员并不是动态库的绝对路径,而只是动态库的文件名。 这个问题通常是由于动态库加载方式导致的。在 Linux 中,动态库可以通过 `LD_LIBRARY_PATH` 环境变量来指定搜索路径。如果动态库加载时,其路径在 `LD_LIBRARY_PATH` 中,那么 `dli_fname` 中就只包含动态库的文件名,而不是绝对路径。 为了解决这个问题,可以使用 `dl_iterate_phdr` 函数遍历当前进程中加载的所有动态库,然后通过比较动态库的基地址和 `dli_fbase` 成员来找到相应的动态库,从而获取其绝对路径。下面是一个示例代码: ```c #include <dlfcn.h> #include <link.h> #include <stdio.h> #include <string.h> void* func; int callback(struct dl_phdr_info* info, size_t size, void* data) { if (func >= (void*)info->dlpi_addr && func < (void*)(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr + info->dlpi_phdr[0].p_memsz)) { printf("Library path: %s\n", info->dlpi_name); return 1; } return 0; } int main() { func = dlsym(RTLD_DEFAULT, "some_function"); struct Dl_info info; if (dladdr(func, &info)) { if (info.dli_fname[0] != '/') { dl_iterate_phdr(callback, NULL); } else { printf("Library path: %s\n", info.dli_fname); } } return 0; } ``` 这个示例代码中,我们首先使用 `dlsym` 函数获取某个函数的地址,然后使用 `dladdr` 函数获取该函数所在的动态库的信息。如果 `dli_fname` 成员中包含了绝对路径,那么直接打印即可。否则,我们就使用 `dl_iterate_phdr` 函数遍历动态库,找到包含该函数动态库,最后打印其绝对路径。 需要注意的是,使用 `dl_iterate_phdr` 函数需要链接 `libdl` 库。在编译时需要加上 `-ldl` 选项
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值