linux 中的动态库加载

在Linux中可以动态加载库,其使用方法如下:


函数 描述

#include<dlfcn.h>头文件定义如下四个函数
dlopen 使对象文件可被程序访问
dlsym   获取执行了 dlopen 函数的对象文件中的符号的地址
dlerror  返回上一次出现错误的字符串错误
dlclose 关闭目标文件

 ELF = Executable and Linkable Format,可执行连接格式,是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。扩展名为elf。

 

dlopen函数返回一个句柄,该句柄用于后续的 API 调用。

dlopen的原型为:#include<dlfcn.h>
void *dlopen( const char *file, int mode );

 

        该过程首先是调用 dlopen,提供要访问的文件对象和模式。调用 dlopen 的结果是稍候要使用的对象的句柄。mode 参数通知动态链接器何时执行再定位。有两个可能的值。第一个是 RTLD_NOW,它表明动态链接器将会在调用 dlopen 时完成所有必要的再定位。第二个可选的模式是 RTLD_LAZY,它只在需要时执行再定位。这是通过在内部使用动态链接器重定向所有尚未再定位的请求来完成的。这样,动态链接器就能够在请求时知晓何时发生了新的引用,而且再定位可以正常进行。后面的调用无需重复再定位过程。

还可以选择另外两种模式,它们可以按位 OR 到 mode 参数中。RTLD_LOCAL 表明其他任何对象都无法使加载的共享对象的符号用于再定位过程。如果这正是您想要的的话(例如,为了让共享的对象能够调用原始进程映像中的符号),那就使用 RTLD_GLOBAL 吧。

dlopen 函数还会自动解析共享库中的依赖项。这样,如果您打开了一个依赖于其他共享库的对象,它就会自动加载它们。

 

有了 ELF 对象的句柄,就可以通过调用 dlsym 来识别这个对象内的符号的地址了。该函数采用一个符号名称,如对象内的一个函数的名称。

返回值为对象符号的解析地址:

void *dlsym( void*restrict handle, const char *restrict name );

如果调用该 API 时发生了错误,可以使用 dlerror 函数返回一个表示此错误的人类可读的字符串。该函数没有参数,它会在发生前面的错误时返回一个字符串,在没有错误发生时返回 NULL:

char *dlerror();

最后,如果无需再调用共享对象的话,应用程序可以调用 dlclose 来通知操作系统不再需要句柄和对象引用了。它完全是按引用来计数的,所以同一个共享对象的多个用户相互间不会发生冲突(只要还有一个用户在使用它,它就会待在内存中)。

任何通过已关闭的对象的 dlsym 解析的符号都将不再可用。

char *dlclose( void *handle );

********************************************************************************************************************************
1. 先生成一个动态库libtest.so

/* test.c */
#include <stdio.h>
#include <stdio.h>
void test1(int no)
{
     printf("*****************************************\n");
     printf("This is test1, the number is %d.\n", no);
     printf("*****************************************\n");
}
void test2(char *str)
{
     printf("=========================================\n");
     printf("This is test2, the string is %s.\n", str);
     printf("=========================================\n");
}
"**



编译库:
gcc -fPIC -shared -o libtest.so test.c
这样就可以生成libtest.so动态库。
在这个库里,定义个两个函数test1,test2,下面将在程序中加载libtest.so,然后调用test1,test2。

2. 动态加载libtest.so
/* main.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dlfcn.h> /* 必须加这个头文件 */
#include <assert.h>
int main()
{
     void *handler = dlopen("./libtest.so", RTLD_NOW);
     assert(handler != NULL);
     void (*test1)(int) = dlsym(handler, "test1");
     assert(test1 != NULL);
     void (*test2)(char *) = dlsym(handler, "test2");
     assert(test2 != NULL);
     (*test1)(10);
     (*test2)("hello");
     dlclose(handler);
     return 0;
}

在这个程序中,dlopen函数用来打开一个动态库,其返回一个void *的指针,如果失败,返回NULL。
dlsym返回一个动态库中的一个函数指针,如果失败,返回NULL。
dlclose关闭指向动态库的指针。
编译的时候需要加上 -ldl

gcc -Wall-rdynamic-o main main.c -ldl(编译时要使用共享库dl 其中有dlopen dlsynm dlerror dlclose 函数)
选项-rdynamic 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用dlopen 来实现向后跟踪)。-ldl 表明一定要将dllib 链接于该程序。

运行main,将会看到调用test1,和test2的结果

 在linux下最方便的解决方案是拷贝libtest.so到绝对目录 /lib 下(但是,要是超级用户才可以,因此要使用sudo哦,亲)。就可以生成可执行程序了
cp /home/**/libtest.so ./lib

==================================================================================
第一种是修改系统文件:

  在/etc/ld.so.conf文件中指定了默认的动态链接库查找路径,我的/etc/ld.so.conf文件内容是这样的include /etc/ld.so.conf.d/*.conf

  也就是说它间接的指定了定义路径的文件,我们只需要把需要的路径加到/etc/ld.so.conf.d目录下的任何一个文件中,再运行ldconfig就可以了,但为了容易理解,最好是找一个相关的文件,或者重新建立一个文件,把需要添加的路径写入然后运行ldconfig

  第二种是运用变量LD_LIBRARY_PATH:

  把需要添加的路径加入到LD_LIBRARY_PATH中,注意如果多于一个要用冒号隔开。如:export LD_LIBRARY_PATH=/usr/local/lib/minigui

  第三种是编译的时候设定:

  在编译源码的时候可以用参数:-Wl, -rpath指定动态搜索的路径即可。



第二个例子:

dltest.c:

#include <stdio.h>

int printStr(char *str)
{
	printf("***************************\n");
	printf("%s\n", str);
	printf("===========================\n");
	return 0;
}
dlmain.c

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


typedef int (*FUNC)(char*);


int main(int argc, char *argv[])
{
	char outStr[20] = {"test dl."};
	char *error = NULL;
	void *dlHandle = NULL;
	FUNC pFunc; 

	dlHandle = dlopen("./dltest.so", RTLD_LAZY);
	if(dlHandle == NULL)
	{
		printf("dlHandle is null\n");
		return -1;
	}

	pFunc = (FUNC)dlsym(dlHandle, "printStr");
	if((error = dlerror()) != NULL)
	{
		printf("dlsym error(%s).\n", error);
		return -1;
	}

	pFunc(outStr);

	dlclose(dlHandle);
	
	return 0;
}

/* Makefile */

all:
     gcc -o dltest.o -c dltest.c
     gcc -fPIC -shared dltest.o -o dltest.so
     gcc -rdynamic -ldl dlmain.c -o dlmain

http://blog.youkuaiyun.com/cswodi/article/details/7710581

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值