`
844604778
  • 浏览: 550962 次
文章分类
社区版块
存档分类
最新评论

如何读取动态链接库中的符号表,并调用

 
阅读更多
我们知道动态加载的函数库是一类函数库,他可以在程序运行过程中的任何时间加载。它们特别适合在函数中加载一些模块和plugin扩展模块的场合,因为它可以在当程序需要某个plugin模块时才动态的加载。例如,Pluggable AuthenticationModules(PAM)系统就是用动态加载函数库来使得管理员可以配置和重新配置身份验证信息。
Linux系统下,DL函数库与其他函数库在格式上没有特殊的区别,我们前面提到过,它们创建的时候是标准的object格式。主要的区别就是这些函数库不是在程序链接的时候或者启动的时候加载,而是通过一个API来打开一个函数库,寻找符号表,处理错误和关闭函数库。
通常C语言环境下,需要包含这个头文件dlfcn.h
Linux中使用的函数和Solaris中一样,都是dlpoen() API。当时不是所有的平台都使用同样的接口,例如HP-UX使用hl_load()机制,而Windows平台用另外的其他的调用接口。如果你的目的是使得你的代码有很强的移植性,你应该使用一些wrapping函数库,这样的wrapping函数库隐藏不同的平台的接口区别。一种方法是使用glibc函数库中的对动态加载模块的支持,它使用一些潜在的动态加载函数库界面使得它们可以夸平台使用。具体可以参http://developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html. 另外一个方法是使用libltdl,是GNUlibtool的一部分,可以进一步参考CORBA相关资料。

1) dlopen()

dlopen函数打开一个函数库然后为后面的使用做准备。C语言原形是:
void * dlopen(const char *filename, int flag);

如果文件名filename是以“/”开头,也就是使用绝对路径,那么dlopne就直接使用它,而不去查找某些环境变量或者系统设置的函数库所在的目录了。否则dlopen()就会按照下面的次序查找函数库文件:
1. 环境变量LD_LIBRARY指明的路径。

2. /etc/ld.so.cache中的函数库列表。

3. /lib目录,然后/usr/lib。不过一些很老的a.out的loader则是采用相反的次序,也就是先查/usr/lib,然后是/lib。

Dlopen()函数中,参数flag的值必须是RTLD_LAZY或者RTLD_NOW,RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,而RTLD_NOW的含义是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'。

如果有好几个函数库,它们之间有一些依赖关系的话,例如X依赖Y,那么你就要先加载那些被依赖的函数。例如先加载Y,然后加载X。

dlopen()函数的返回值是一个句柄,然后后面的函数就通过使用这个句柄来做进一步的操作。如果打开失败dlopen()就返回一个NULL。如果一个函数库被多次打开,它会返回同样的句柄。



如果一个函数库里面有一个输出的函数名字为_init,那么_init就会在dlopen()这个函数返回前被执行。我们可以利用这个函数在我的函数库里面做一些初始化的工作。我们后面会继续讨论这个问题的。

2) dlerror()
通过调用dlerror()函数,我们可以获得最后一次调用dlopen(),dlsym(),或者dlclose()的错误信息。
3) dlsym()

如果你加载了一个DL函数库而不去使用当然是不可能的了,使用一个DL函数库的最主要的一个函数就是dlsym(),这个函数在一个已经打开的函数库里面查找给定的符号。这个函数如下定义:

void * dlsym(void *handle, char *symbol);

函数中的参数handle就是由dlopen打开后返回的句柄,symbol是一个以NIL结尾的字符串。
如果dlsym()函数没有找到需要查找的symbol,则返回NULL。如果你知道某个symbol的值不可能是NULL或者0,那么就很好,你就可以根据这个返回结果判断查找的symbol是否存在了;不过,如果某个symbol的值就是NULL,那么这个判断就有问题了。标准的判断方法是先调用dlerror(),清除以前可能存在的错误,然后调用dlsym()来访问一个symbol,然后再调用dlerror()来判断是否出现了错误。一个典型的过程如下:

dlerror(); /* clear error code */

s = (actual_type) dlsym(handle, symbol_being_searched_for);

if ((err = dlerror()) != NULL)

{

/* handle error, the symbol wasn't found */

}

else

{

/* symbol found, its value is in s */

}



4) dlclose()

dlopen()函数的反过程就是dlclose()函数,dlclose()函数用力关闭一个DL函数库。Dl函数库维持一个资源利用的计数器,当调用dlclose的时候,就把这个计数器的计数减一,如果计数器为0,则真正的释放掉。真正释放的时候,如果函数库里面有_fini()这个函数,
则自动调用_fini()这个函数,做一些必要的处理。Dlclose()返回0表示成功,其他非0值表示错误。

5) DL Library Example

下面是一个例子。例子中调入math函数库,然后打印2.0的余弦函数值。例子中每次都检查是否出错。是个不错的范例:
  1. #include<stdio.h>
    #include <dlfcn.h>
    #include <stdlib.h>
    intmain(intargc,char*argv){
  2. void*handle;
  3. char*error;
  4. double(*cosine)(double);
  5. handle=dlopen("/lib/libm.so.6",RTLD_LAZY);
  6. if(!handle){
  7. fputs(dlerror(),stderr);
  8. exit(1);
  9. }
  10. cosine=dlsym(handle,"cos");
  11. if((error=dlerror())!=NULL){
  12. fputs(error,stderr);
  13. exit(1);
  14. }
  15. printf("%f",(*cosine)(2,0));
  16. dlclose(handle);
  17. return0;
  18. }
  1. 如果这个程序名字叫cosine.c,那么用下面的命令来编译:
    gcc -o cosine cosine.c -ldl
  2. 使用-ldl选项指明生成的对象模块需要使用共享库
  3. 注意事项:

    1.dlsym返回的指针是无类型的,要转换的指定的函数的类型。
    2.使用函数指针时的写法:(*pfunc)(word); 不能直接写成 pfunc(word);会段错误的
    3.C还不支持默认参数,写show_help时不能给msg以NULL的默认值
    3.编译时要使用共享库dl 其中有dlopen dlsynm dlerrordlclose 函数

    使用命令:

    ./mydlopen ./dlopen.so say aaaabbbdddd

    ------------------------------------分割------------------------------------------

    说明:pfunc = (int (*)(char *))dlsym(handle,func);

    其中char *是将参数强转,因为int (*pfunc)(char *str);是这样定义的。

    如果int (*pfunc)(char *str,int number);这样定义,那么强转的时候应该这样写:

    pfunc = (int (*)(char *,int))dlsym(handle,func);

    --------------------------------------分割-----------------------------------------

    编译的时候的说明,比如现在的文件目录树如下:

    1.work/

    2.|--head.h

    3.|--main.c

    4.|--func.c

    5.|--func2.c

    这个时候欲将func2.c编译成动态链接库dlfunc.so,而func2.c中的函数用到了head.h和func.c里面的变量和函数,这个时候应该使用命令:

    gcc -o dlfunc.so -shared func2.c func.c -I./

    这样就生成了dlfunc.so。

    然后编译连接主程序。

    gcc -o test main.c -ldl -I./

    就行了。


分享到:
评论

相关推荐

    易语言程序免安装版下载

     静态编译后的易语言可执行程序(exe)和动态链接库(dll),运行时不再依赖任何支持库文件,文件尺寸更小(相对以前的独立编译),PE结构更合理(取消了“易格式体”),加载速度更快,而且有效解决了“病毒误报”和...

    delphi 开发经验技巧宝典源码

    0057 动态链接库的创建与调用 36 0058 String类型和Pchar类型的用法和区别 36 0059 如何捕获异常 37 0060 TStrings与TStringList的使用 37 0061 如何实现窗体文件转换 37 第3章 程序算法 39 3.1 计算类...

    delphi 开发经验技巧宝典源码06

    0057 动态链接库的创建与调用 36 0058 String类型和Pchar类型的用法和区别 36 0059 如何捕获异常 37 0060 TStrings与TStringList的使用 37 0061 如何实现窗体文件转换 37 第3章 程序算法 39 3.1 计算类...

    Windows内核安全与驱动开发光盘源码

    1.3.5 设置Windows内核符号表 12 1.3.6 实战调试first 13 第2章 内核编程环境及其特殊性 16 2.1 内核编程的环境 16 2.1.1 隔离的应用程序 16 2.1.2 共享的内核空间 17 2.1.3 无处不在的内核模块 18 2.2 数据...

    Windows内核安全驱动开发(随书光盘)

    1.3.5 设置Windows内核符号表 12 1.3.6 实战调试first 13 第2章 内核编程环境及其特殊性 16 2.1 内核编程的环境 16 2.1.1 隔离的应用程序 16 2.1.2 共享的内核空间 17 2.1.3 无处不在的内核模块 18 2.2 数据...

    perl 程序开发教程

    Perl 5 教程 by flamephoenix ... 本教程欢迎下载、转载和链接,转载必须保留本说明并保持文章完整。本教程不允许以任何形式用于商业用途。 对本教程有何疑问、更正或建议请告诉我,谢谢。 flamephoenix

    Windows 系统错误代码简单分析

     0214 附加到此程序或动态链接模块的动态链接模块太多。  0215 无法嵌套调用 LoadModule。  0216 图像文件 %1 有效,但不适用于本机类型。  0230 管道状态无效。  0231 所有的管道实例都处于忙状态。 ...

    Excel VBA实用技巧大全 附书源码

    03011引用工作表名称中包含特定字符串的工作表 03012获取工作表名称 03013获取全部工作表名称 03014判断某个表是否为工作表 03015获取工作表的显示状态 03016获取工作表的保护状态 03017判断工作表是否处于筛选模式 ...

    Perl5语言全教程

    2、链接和符号链接函数 3、文件许可权函数 4、其他属性函数 四、使用DBM文件 第十二章 Perl5中的引用(指针) 一、引用简介 二、使用引用 三、使用反斜线(\)操作符 四、引用和数组 五、多维数组 六、子程序的引用...

    JavaScript完全自学宝典 源代码

    Calculate1.java 计算浮点数运算结果并调用页面中JavaScript函数的Applet。 Calculate1.class Calculate1.java的字节码文件。 第16章(\c16) 示例描述:介绍JavaScript访问本地文件的各种方法。 16.1....

    C#微软培训资料

    18.2 在 C #代码中调用 C++和 VB 编写的组件 .240 18.3 版 本 控 制 .249 18.4 代 码 优 化 .252 18.5 小 结 .254 第五部分 附 录 .255 附录 A 关 键 字.255 附录 B 错 误 码.256 附录 C .Net 名字空间...

    寒江独钓-Windows内核安全编程(高清完整版).part7

    1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 ...

    寒江独钓-Windows内核安全编程(高清完整版).part4

    1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 ...

    寒江独钓-Windows内核安全编程(高清完整版).part6

    1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 ...

    寒江独钓-Windows内核安全编程(高清完整版).part5

    1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 ...

    寒江独钓-Windows内核安全编程(高清完整版).part3

    1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 ...

    寒江独钓-Windows内核安全编程(高清完整版).part1

    1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 ...

Global site tag (gtag.js) - Google Analytics