c/c++ 直接使用动态库 dlopen
c/c++ 直接使用动态库 dlopen
- dlopen void *dlopen(const char *filename, int flag); 装载动态库
- dlclose int dlclose(void *handle);
- dlerror char *dlerror(void); 返回可读字符串
- dladdr
- dlsym void *dlsym(void *handle, const char *symbol);
- dlvsym
- #include <stdio.h>
- #include <stdlib.h>
- #include <dlfcn.h>
- //gcc flag
- // gcc t_dlopen.c -ldl
- int
- main(int argc, char **argv)
- {
- void *handle;
- double (*cosine)(double);
- char *error;
- handle = dlopen("libm.so", RTLD_LAZY);
- if (!handle) {
- fprintf(stderr, "%s\n", dlerror());
- exit(EXIT_FAILURE);
- }
- dlerror(); /* Clear any existing error */
- /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
- would seem more natural, but the C99 standard leaves
- casting from "void *" to a function pointer undefined.
- The assignment used below is the POSIX.1-2003 (Technical
- Corrigendum 1) workaround; see the Rationale for the
- POSIX specification of dlsym(). */
- *(void **) (&cosine) = dlsym(handle, "cos");
- if ((error = dlerror()) != NULL) {
- fprintf(stderr, "%s\n", error);
- exit(EXIT_FAILURE);
- }
- printf("%f\n", (*cosine)(0.0));
- dlclose(handle);
- exit(EXIT_SUCCESS);
- }
编译使用库 libdlgcc t_dlopen.c -ldl输出
- 1.000000
Internet 浏览器用户非常熟悉插件的概念。从 Web
上下载插件,通常这些插件为浏览器的音频、视频以及特殊效果提供增强支持。一般来讲,在不更改原有应用程序的情况下,插件为现有应用程序提供新功能。
DLL
是程序函数,它们在设计和构建应用程序时为该程序所知。设计应用程序的主程序时使用程序框架或底板,这些程序框架或底板在运行时选择性地装入所需的
dll,这些 dll
位于磁盘上同主程序分离的一些文件中。这一打包和动态装入提供了灵活的升级、维护、以及许可策略。
随 Linux 一起交付的还有几千条命令和应用程序,它们至少都需要 libc
库函数。如果 libc
函数与每一个应用程序都打包在一起,那么磁盘上将会出现几千个相同函数的副本。Linux
构建这些应用程序,以使用通常所需的系统库的单个系统级副本,而不浪费磁盘空间。Linux
甚至做得更好,每个需要公共系统库函数的进程使用单个的系统级内的副本,一次性将该副本装入到内存并为各进程所共享。
在 Linux 中,插件和 dll
以动态库形式实现。本文的余下部分是在应用程序运行之后使用动态库更改该应用程序的示例。
Linux 动态链接
Linux
中的应用程序以以下两种方式之一链接到外部函数:要么在构建时与静态库(
lib*.a )
静态地链接,并且将库代码包含在该应用程序的可执行文件里;要么在运行时与共享库(
lib*.so )
动态地链接。通过动态链接装入器,将动态库映射进应用程序的可执行内存中。在启动应用程序之前,动态链接装入器将所需的共享目标库映射到应用程序的内存,或者使用系统共享的目标并为应用程序解析所需的外部引用。现在应用程序就可以运行了。
作为示例,下面有一个演示 Linux
中对动态链接库的缺省使用的小程序:
- main()
- {
- printf("Hello world
- ");
- }
当使用 gcc 编译 hello.c 时,就创建了一个名为
a.out
的可执行文件。通过使用 Linux 命令
ldd
a.out (该命令打印出共享库的相互依赖性),可以看出所需的共享库是:
libc.so.6 => /lib/libc.so.6 (0x4001d000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) |
使用相同的动态链接装入器在应用程序运行之后将 dll
映射进应用程序的内存。通过使用 Linux
动态装入器例程,应用程序控制装入哪一个动态库以及调用库中的哪一个函数,以执行装入和链接以及返回所需入口点的地址。
Linux dll 函数
Linux 提供 4 个库函数(
dlopen ,
dlerror ,
dlsym 和
dlclose ),一个 include
文件(
dlfcn.h )以及两个共享库(静态库
libdl.a 和动态库
libdl.so ),以支持动态链接装入器。这些库函数是:
- dlopen
将共享目标文件打开并且映射到内存中,并且返回句柄 - dlsym返回一个指向被请求入口点的指针
- dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII
字符串的指针 - dlclose关闭句柄并且取消共享目标文件的映射
动态链接装入器例程 dlopen
需要在文件系统中查找共享目标文件以打开文件并创建句柄。有 4
种方式用以指定文件的位置:
- dlopen call 中的绝对文件路径
- 在 LD_LIBRARY_PATH 环境变量中指定的目录中
- 在 /etc/ld.so.cache 中指定的库列表之中
- 先在 /usr/lib 之中,然后在 /lib 之中
dll 示例:小的 C 程序和 dlTest
动态链接装入器示例程序是一个小的 C 程序,该程序被设计用来练习 dl
例程。该程序基于每个人都编写过的一个 C 程序,它将“Hello
World”打印到控制台上。最初打印的消息是“HeLlO
WoRlD”。该测试程序链接到再次打印该消息的两个函数上:第一次都用大写字符,第二次都用小写字符。
以下是该程序的概要:
- 定义 dll include 文件
dlfcn.h 和所需的变量。
至少需要这些变量:- 到共享库文件的句柄
- 指向被映射函数入口点的指针
- 指向错误字符串的指针
- 打印初始消息,“HeLlO WoRlD”。
- 使用绝对路径“/home/dlTest/UPPERCASE.so”和选项
RTLD_LAZY,
dlopen 打开 UPPERCASE dll
的共享目标文件并返回句柄。- 选项 RTLD_LAZY 推迟解析 dll 的外部引用,直到 dll 被执行。
- 选项 RTLD_NOW 在
dlopen
返回之前解析所有的外部引用。
- dlsym 返回入口点 printUPPERCASE 的地址。
- 调用 printUPPERCASE 并且打印修改过的消息“HELLO WORLD”。
- dlclose 关闭到 UPPERCASE.so
的句柄,并且从内存中取消 dll 映射。 - dlopen 使用基于环境变量 LD_LIBRARY_PATH
的相对路径查找共享目标路径,来打开 lowercase dll 的共享目标文件
lowercase.so,并且返回句柄。 - dlsym 返回入口点 printLowercase 的地址。
- 调用 printLowercase 并且打印修改过的信息“hello world”。
- dlclose 关闭到 lowercase.so
的句柄,并且从内存中取消 dll 映射。
注意,每次调用
dlopen 、
dlsym 或
dlclose 之后,调用
dlerror
以获取最后的错误信息,并且打印该错误信息字符串。以下是 dlTest
的测试运行:
dlTest 2-Original message HeLlO WoRlD dlTest 3-Open Library with absolute path return-(null)- dlTest 4-Find symbol printUPPERCASE return-(null)- HELLO WORLD dlTest 5-printUPPERCASE return-(null)- dlTest 6-Close handle return-(null)- dlTest 7-Open Library with relative path return-(null)- dlTest 8-Find symbol printLowercase return-(null)- hello world dlTest 9-printLowercase return-(null)- dlTest 10-Close handle return-(null)- |
完整的 dlTest.c、UPPERCASE.c 和 lowercase.c
源代码清单在本文后面的
清单里。
构建 dlTest
启用运行时动态链接需要三步:
- 将 dll 编译为位置无关代码
- 创建 dll 共享目标文件
- 编译主程序并同 dl 库相链接
编译 UPPERCASE.c 和 lowercase.c 的 gcc 命令包含 -fpic 选项。选项
-fpic 和 -fPIC
导致生成的代码是位置无关的,重建共享目标库需要位置无关。-fPIC
选项产生位置无关的代码,这类代码支持大偏移。用于 UPPERCASE.o 和
lowercase.o 的第二个 gcc 命令,带有 -shared
选项,该选项产生适合于动态链接的共享目标文件 a*.so。
用于编译和执行 dltest 的 ksh 脚本如下:
|
结束语
创建能在运行时被动态链接到 Linux
系统上的应用程序的共享目标代码是一项非常简单的练习。应用程序通过使用对动态链接装入器的
dlopen、dlsym 和 dlclose
函数调用来获取对共享目标文件的访问。dlerror
以字符串的形式返回任何错误,这些错误信息字符串描述 dl
函数碰到的最后一个错误。在运行时,主应用程序使用绝对路径或相对于
LD_LIBRARY_PATH 的相对路径找到共享目标库,并且请求所需的 dll
入口点的地址。当需要时,也可对 dll
进行间接函数调用,最后,关闭到共享目标文件的句柄,并且从内存中取消该目标文件映射,使之不可用。
使用附加选项 -fpic 或 -fPIC
编译共享目标代码,以产生位置无关的代码,使用 -shared
选项将目标代码放进共享目标库中。
Linux
中的共享目标代码库和动态链接装入器向应用程序提供了额外的功能。减少了磁盘上和内存里的可执行文件的大小。可以在需要时,装入可选的应用程序功能,可以在无须重新构建整个应用程序的情况下修正缺陷,并且应用程序可以包含第三方的插件。
清单(应用程序和 dll)
dlTest.c:
- /*************************************************************/
- /* Test Linux Dynamic Function Loading */
- /* */
- /* void *dlopen(const char *filename, int flag) */
- /* Opens dynamic library and return handle */
- /* */
- /* const char *dlerror(void) */
- /* Returns string describing the last error. */
- /* */
- /* void *dlsym(void *handle, char *symbol) */
- /* Return pointer to symbol's load point. */
- /* If symbol is undefined, NULL is returned. */
- /* */
- /* int dlclose (void *handle) */
- /* Close the dynamic library handle. */
- /* */
- /* */
- /* */
- /*************************************************************/
- #include<stdio.h>
- #include <stdlib.h>
- /* */
- /* 1-dll include file and variables */
- /* */
- #include <dlfcn.h>
- void *FunctionLib; /* Handle to shared lib file */
- int (*Function)(); /* Pointer to loaded routine */
- const char *dlError; /* Pointer to error string */
- main( argc, argv )
- {
- int rc; /* return codes */
- char HelloMessage[] = "HeLlO WoRlD\n";
- /* */
- /* 2-print the original message */
- /* */
- printf(" dlTest 2-Original message \n");
- printf("%s", HelloMessage);
- /* */
- /* 3-Open Dynamic Loadable Libary with absolute path */
- /* */
- FunctionLib = dlopen("/home/dlTest/UPPERCASE.so",RTLD_LAZY);
- dlError = dlerror();
- printf(" dlTest 3-Open Library with absolute path return-%s- \n", dlError);
- if( dlError ) exit(1);
- /* */
- /* 4-Find the first loaded function */
- /* */
- Function = dlsym( FunctionLib, "printUPPERCASE");
- dlError = dlerror();
- printf(" dlTest 4-Find symbol printUPPERCASE return-%s- \n", dlError);
- if( dlError ) exit(1);
- /* */
- /* 5-Execute the first loaded function */
- /* */
- rc = (*Function)( HelloMessage );
- printf(" dlTest 5-printUPPERCASE return-%s- \n", dlError);
- /* */
- /* 6-Close the shared library handle */
- /* Note: after the dlclose, "printUPPERCASE" is not loaded */
- /* */
- rc = dlclose(FunctionLib);
- dlError = dlerror();
- printf(" dlTest 6-Close handle return-%s-\n",dlError);
- if( rc ) exit(1);
- /* */
- /* 7-Open Dynamic Loadable Libary using LD_LIBRARY path */
- /* */
- FunctionLib = dlopen("lowercase.so",RTLD_LAZY);
- dlError = dlerror();
- printf(" dlTest 7-Open Library with relative path return-%s- \n", dlError);
- if( dlError ) exit(1);
- /* */
- /* 8-Find the second loaded function */
- /* */
- Function = dlsym( FunctionLib, "printLowercase");
- dlError = dlerror();
- printf(" dlTest 8-Find symbol printLowercase return-%s- \n", dlError);
- if( dlError ) exit(1);
- /* */
- /* 8-execute the second loaded function */
- /* */
- rc = (*Function)( HelloMessage );
- printf(" dlTest 9-printLowercase return-%s- \n", dlError);
- /* */
- /* 10-Close the shared library handle */
- /* */
- rc = dlclose(FunctionLib);
- dlError = dlerror();
- printf(" dlTest 10-Close handle return-%s-\n",dlError);
- if( rc ) exit(1);
- return(0);
- }
UPPERCASE.c:
|
lowercase.c
|
- man dlopen
- 为 Linux 应用程序编写 DLL http://www.ibm.com/developerworks/cn/linux/sdk/dll/index.html
c/c++ 直接使用动态库 dlopen的更多相关文章
- C 高级编程3 静态库与动态库
http://blog.csdn.net/Lux_Veritas/article/details/11934083http://www.cnblogs.com/catch/p/3857964.html ...
- Linux 动态库的编译和使用
1. 动态链接库简介 动态库又叫动态链接库,是程序运行的时候加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序.动态库是目标文件的集合,目标文件在动态库中的组织方式是按特殊的方式组 ...
- Linux下c函数dlopen实现加载动态库so文件代码举例
dlopen()是一个强大的库函数.该函数将打开一个新库,并把它装入内存.该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的.这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了. ...
- linux c编程调用系统的动态库时,要使用dlopen等函数吗?
同问 linux c编程调用系统的动态库时,要使用dlopen等函数吗? 2012-11-27 21:55 提问者: hnwlxyzhl 我来帮他解答 满意回答 2012-12-07 09:08 li ...
- 使用dlopen加载动态库
目录 概述 接口 C CMakeLists.txt src/main.c src/add.c ./dlopen_test C++ CMakeLists.txt src/main.cpp src/add ...
- Android 5.0 到 Android 6.0 + 的深坑之一 之 .so 动态库的适配
(原创:http://www.cnblogs.com/linguanh) 目录: 前序 一,问题描述 二,为何会如此"无情"? 三,目前存在该问题的知名SDK 四,解决方案,1 对 ...
- C++ 系列:静态库与动态库
转载自http://www.cnblogs.com/skynet/p/3372855.html 这次分享的宗旨是——让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择 ...
- C++静态库与动态库
C++静态库与动态库 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一 ...
- Linux动态库的编译与使用 转载
http://hi.baidu.com/linuxlife/blog/item/0d3e302ae2384d3a5343c1b1.html Linux下的动态库以.so为后缀,我也是初次在Linux下 ...
随机推荐
- 在 vb中 "end","unload me","exit sub" 之间的区别
之前就想过这个问题,这么熟悉的几个东西居然对他们分析的不是很透彻. “End” 跟 “Unload Me” 在敲程序 的时候经常敲到,“exit sub” 更是熟悉,下面,解析: End ...
- Android 传感器开发
如今的智能手机都配备了各种各样的传感器,本文将介绍Android SDK提供的传感器开发接口,并通过简单实例展示怎样使用这些接口. Andriod SDK传感器相关类 android SDK提供的与传 ...
- NGUI出现Shader wants normals, but the mesh UIAtlas doesn't have them
NGUI出现Shader wants normals, but the mesh UIAtlas doesn't have them,没有网格法线,打开UI Root上 UIPanel组建上的 Nor ...
- linux BC命令行计算器
1. 基本使用: $ bc <<< 5*4 20 $ bc <<< 5+4 9 $ bc <<< 5-4 1 或者 $ echo "5* ...
- dll文件32位64位检测工具以及Windows文件夹SysWow64的坑(很详细,还有自动动手编程探测dll)
阅读目录 dll文件不匹配导致数据库无法启动 究竟是System32还是SysWow64 区分dll文件32位64位的程序让我倍感迷惑 再次判断究竟是System32还是SysWow64——意想不到的 ...
- Silk Icons —— 再来 700 个免费小图标
http://mp.weixin.qq.com/mp/appmsg/show?__biz=MjM5NzM0MjcyMQ==&appmsgid=10000977&itemidx=2&am ...
- android5.0(Lollipop) BLE Peripheral深入理解系统篇之提高篇
上一篇文章讲到了广播之前系统需要进行的准备工作,那接下来我们就来真正的启动广播. 首先还是先看一下上一篇文章结束的地方: @Override public void onClientRegistere ...
- iTunes Store:隐藏和取消隐藏已购项目
使用 Mac 或 PC 上的 iTunes 来隐藏或取消隐藏已购项目. 如何隐藏已购项目 在 Mac 或 PC 上打开 iTunes. 从 Store 菜单中,选取商店 > 登录,然后输入您的 ...
- MYSQL获取自增主键【4种方法】
通常我们在应用中对mysql执行了insert操作后,需要获取插入记录的自增主键.本文将介绍java环境下的4种方法获取insert后的记录主键auto_increment的值: 通过JDBC2.0提 ...
- 对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点-----层序遍历的应用题
题目:对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点:如果当前节点已经是该层的最后一个节点,则将pNext指针指向NULL:给出程序实现,并分析时间复杂度和空间复杂度. ...