linux dlopen 相关

linux dlopen 相关dlopen 3 Linux 手册页名称 dladdr dlclose dlerror dlopen dlsym dlvsym 动态链接加载器的编程接口概要 include lt nbsp dlfcn h nbsp gt void dlopen constchar nbsp filename nbsp int nbsp flag nbsp char dlerror void void dls

dlopen(3) – Linux手册页

名称

dladdr,dlclose,dlerror,dlopen,dlsym,dlvsym – 动态链接加载器的编程接口

概要

#include < dlfcn.h >

void * dlopen(const char * filename ,int flag );

char * dlerror(void);

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

int dlclose(void * handle );

-ldl链接。

描述

四个函数dlopen(),dlsym(),dlclose(),dlerror()实现了动态链接加载器的接口。

dlerror获得()

函数dlerror()返回一个人类可读的字符串,描述自从上次调用dlerror()以来dlopen(),dlsym()或 dlclose()发生的最新错误。如果从初始化或上次调用以来没有发生错误,它将返回NULL。

dlopen的()

函数dlopen()加载由以null结尾的字符串文件名命名的动态库文件,并为动态库返回一个不透明的“句柄”。如果文件名为NULL,则返回的句柄用于主程序。如果filename包含一个斜线(“/”),则它被解释为(相对或绝对)路径名。否则,动态链接器将按如下方式搜索库(有关更多详细信息,请参阅ld.so(8)):

Ø

(仅限ELF)如果调用程序的可执行文件包含DT_RPATH标记,并且不包含DT_RUNPATH标记,则搜索DT_RPATH标记中列出的目录。

Ø

如果在程序启动时,环境变量LD_LIBRARY_PATH被定义为包含冒号分隔的目录列表,则会搜索这些目录。(作为一个安全措施,这个变量在set-user-ID和set-group-ID程序中被忽略。)

Ø

(仅限ELF)如果调用程序的可执行文件包含DT_RUNPATH标记,则搜索该标记中列出的目录。

Ø

将检查缓存文件/etc/ld.so.cache(由ldconfig(8)维护)以查看它是否包含文件名条目。

Ø

搜索目录/ lib/ usr / lib按此顺序)。

如果库对其他共享库有依赖关系,那么它们也会由动态链接程序使用相同的规则自动加载。(这个过程可能会递归发生,如果这些库依次具有依赖性,依此类推。)

标志中必须包含以下两个值之一:

RTLD_LAZY执行延迟绑定。只有在执行引用它们的代码时才解析符号。如果符号从未被引用,那么它永远不会被解析。(懒惰绑定仅针对函数引用执行;对于变量的引用总是在加载该库时立即绑定。)RTLD_NOW如果指定了该值,或者环境变量LD_BIND_NOW设置为非空字符串,则在dlopen()返回之前解析库中的所有未定义符号。如果无法完成,则返回错误。以下值中的零个或多个值也可以在标志中进行或运算:RTLD_GLOBAL由该库定义的符号将可用于随后加载的库的符号解析。RTLD_LOCAL这是RTLD_GLOBAL的反过来,如果既没有指定标志,也是默认值。此库中定义的符号不可用于解决随后加载的库中的引用。RTLD_NODELETE(自glibc 2.2以来)在dlclose()期间不要卸载该库。因此,如果库稍后用dlopen()重新加载,则库的静态变量不会重新初始化 。该标志在POSIX.1-2001中未被指定。RTLD_NOLOAD(自glibc 2.2以来)不要加载库。这可以用来测试库是否已经驻留(dlopen()如果不是,则返回NULL,如果库驻留则返回库)。该标志也可用于提升已加载库的标志。例如,以前加载RTLD_LOCAL的库 可以使用RTLD_NOLOAD |重新打开 RTLD_GLOBAL。该标志在POSIX.1-2001中未被指定。RTLD_DEEPBIND(自glibc 2.3.4开始)将该库中符号的查找范围放在全局范围之前。这意味着一个独立的库将使用它自己的符号而不是全局符号,这些符号包含在已经加载的库中。该标志在POSIX.1-2001中未被指定。如果filename是一个NULL指针,那么返回的句柄就是主程序。当提供给dlsym()时,该句柄将导致在主程序中搜索一个符号,然后在程序启动时加载所有共享库,然后使用dlopen()加载标志为 RTLD_GLOBAL的所有共享库。

库中的外部引用通过使用该库的依赖项列表中的库以及之前使用RTLD_GLOBAL标志打开的其他库来解决 。如果可执行文件与标志“-rdynamic”(或同义词,“–export-dynamic”)链接,则可执行文件中的全局符号也将用于解析动态加载的库中的引用。

如果使用dlopen()再次加载相同的库,则会返回相同的文件句柄。dl库维护库句柄的引用计数,因此动态库不会被释放,直到dlclose()被调用多次,直到dlopen()成功完成为止。在_init()例程,如果存在的话,只调用一次。但随后使用RTLD_NOW调用可能会强制使用先前加载RTLD_LAZY的库的符号解析 。

如果dlopen()因任何原因失败,则返回NULL。

对dlsym()

函数dlsym()采用由dlopen()返回的动态库的“句柄” 和空终止的符号名称,并返回该符号加载到内存中的地址。如果未找到符号,则在指定库或dlopen()加载该库时自动加载的任何 库中,dlsym()返回NULL。(由dlsym()执行的搜索首先通过这些库的依赖树宽度)。由于符号的值实际上可以是NULL(所以dlsym()的NULL返回不需要指示错误),所以正确的方法测试错误是调用dlerror()清除任何旧的错误条件,然后调用dlsym(),然后再次调用dlerror(),将其返回值保存到变量中,并检查此保存的值是否不为NULL。

有两个特殊的伪句柄RTLD_DEFAULTRTLD_NEXT。前者将使用默认库搜索顺序查找所需符号的第一个匹配项。后者将在当前库之后的搜索顺序中查找下一个函数。这使得人们可以在另一个共享库中的函数中提供一个包装。

dlclose()

函数dlclose()递减动态库句柄句柄的引用计数。如果引用计数下降到零,并且没有其他加载的库使用它中的符号,则动态库将被卸载。

函数dlclose()成功时返回0,错误时返回非零值。

过时的符号_init()和_fini()

链接器识别特殊符号_init_fini。如果动态库导出名为_init()的例程,那么在dlopen()返回之前,该代码会在加载后执行。如果动态库导出名为_fini()的例程,那么在库被卸载之前调用该例程。如果您需要避免链接到系统启动文件,可以使用gcc(1)-nostartfiles 命令行选项完成此操作。

不建议使用这些例程或gcc- nostartfiles-nostdlib选项。它们的使用可能会导致不希望的行为,因为构造函数/析构函数将不会执行(除非采取特殊措施)。

相反,库应该使用__attribute __((构造函数))__attribute __((析构函数))函数属性导出例程。有关这些信息,请参阅gcc信息页面。构造函数例程在dlopen()返回之前执行,而析构函数例程在dlclose()返回之前执行 。

Glibc扩展名:dladdr()和dlvsym()

Glibc增加了POSIX未描述的两个功能,其中包含原型

#define _GNU_SOURCE / *请参阅feature_test_macros(7)* / #include < dlfcn.h > int dladdr(void * addr ,Dl_info * info ); void * dlvsym(void * handle ,char * symbol ,char * version );  / *请参阅feature_test_macros(7)* / #include < dlfcn.h > int dladdr(void * addr ,Dl_info * info ); void * dlvsym(void * handle ,char * symbol ,char * version ); 

函数dladdr()接受函数指针并尝试解析它所在的名称和文件。信息存储在Dl_info 结构中:

typedef struct { const char * dli_fname; / *共享对象的路径名 包含地址* / void * dli_fbase; / *共享对象的地址 已加载* / const char * dli_sname; / *地址最近的符号的名称 低于地址 * / void * dli_saddr; / *指定符号的确切地址 在dli_sname * / } Dl_info; const char * dli_fname; / *共享对象的路径名 包含地址* / void * dli_fbase; / *共享对象的地址 已加载* / const char * dli_sname; / *地址最近的符号的名称 低于地址 * / void * dli_saddr; / *指定符号的确切地址 在dli_sname * / } Dl_info;

如果找不到符号匹配addr,则将dli_snamedli_saddr设置为NULL。

dladdr()在错误时返回0,在成功时返回非零值。

函数dlvsym(),自2.1版以来由glibc提供,与dlsym()相同,但将版本字符串作为附加参数。

符合

POSIX.1-2001介绍了dlclose(),dlerror(),dlopen()和dlsym()。

笔记

符号RTLD_DEFAULTRTLD_NEXT被定义< dlfcn.h中 >仅当_GNU_SOURCE包括之前被定义。

由于glibc 2.2.3,atexit(3)可用于注册卸载库时自动调用的退出处理函数。

历史

dlopen接口标准来自SunOS。该系统也有dladdr(),但不包括dlvsym()。

错误

有时候,你传递给dladdr()的函数指针可能会让你感到惊讶。在某些体系结构(特别是i386和x86_64)中,即使用作参数的函数应来自动态链接库,dli_fname和 dli_fbase最终可能会指向您称为dladdr()的对象。

问题在于函数指针在编译时仍然会被解析,但只是指向原始对象的plt(过程链接表)部分(在请求动态链接器解析该符号后调度该调用)。要解决这个问题,你可以尝试将代码编译为位置无关的:然后,编译器不能在编译时间准备好指针,今天的gcc(1)将生成代码,只是从得到的代码中加载最后的符号地址(全局偏移量表)在传递给dladdr()之前的运行时间。

加载数学库,并打印2.0的余弦:

#include < stdio.h > #include < stdlib.h > #include < dlfcn.h > INT main(int argc,char argv) { void * handle; 双(*余弦)(双); char *错误; handle = dlopen(“libm.so”,RTLD_LAZY); if(!handle){ fprintf(stderr,“%s \ n”,dlerror()); 出口(EXIT_FAILURE); } dlerror获得(); / *清除任何现有的错误* / / *写入:cosine =(double(*)(double))dlsym(句柄,“cos”); 似乎更自然,但C99标准离开了 从“void *”转换为未定义的函数指针。 下面使用的任务是POSIX.1-2003(技术文档 勘误1)解决方法; 请参阅 POSIX规范的dlsym()。* / *(void )(&cosine)= dlsym(句柄,“cos”); 如果((error = dlerror())!= NULL){ fprintf(stderr,“%s \ n”,错误); 出口(EXIT_FAILURE); } printf(“%f \ n”,(* cosine)(2.0)); dlclose(手柄); 出口(EXIT_SUCCESS); }stdio.h > #include < stdlib.h > #include < dlfcn.h > INT main(int argc,char argv) { void * handle; 双(*余弦)(双); char *错误; handle = dlopen(“libm.so”,RTLD_LAZY); if(!handle){ fprintf(stderr,“%s \ n”,dlerror()); 出口(EXIT_FAILURE); } dlerror获得(); / *清除任何现有的错误* / / *写入:cosine =(double(*)(double))dlsym(句柄,“cos”); 似乎更自然,但C99标准离开了 从“void *”转换为未定义的函数指针。 下面使用的任务是POSIX.1-2003(技术文档 勘误1)解决方法; 请参阅 POSIX规范的dlsym()。* / *(void )(&cosine)= dlsym(句柄,“cos”); 如果((error = dlerror())!= NULL){ fprintf(stderr,“%s \ n”,错误); 出口(EXIT_FAILURE); } printf(“%f \ n”,(* cosine)(2.0)); dlclose(手柄); 出口(EXIT_SUCCESS); }

如果此程序位于名为“foo.c”的文件中,则可以使用以下命令构建程序:

gcc -rdynamic -o foo foo.c -ldl

导出_init()和_fini()的库需要按如下方式编译,使用bar.c作为示例名称:

gcc -shared -nostartfiles -o bar bar.c

也可以看看

ld(1), ldd(1), dl_iterate_phdr(3), rtld-audit(7), ld.so(8), ldconfig(8)

ld.so信息页面,gcc信息页面,ld信息页面

 

 

 

以上内容  来自    https://linux.die.net/man/3/dlopen  翻译

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233873.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【插件】推荐两个谷歌浏览器的插件[通俗易懂]

    昨天加班到很晚,遇到一个很奇怪的问题,今天才大概定位产生的原因! 很多时候问题很难复现,导致排查问题的效率就不是那么的高效!本来想着写一下这次排查问题的总结,还是后面有时间在写。今天分享两个谷歌浏览器(程序员必备浏览器)的插件,我一直在用,特别好用,所以推荐给你!CSDN中屏蔽百度广告 – Adblock Plus插件我自己有时候会在CSDN写一些技术总结等,每次访问都会百度广告,真是TM…

    2022年2月27日
    163
  • vue 解决跨域问题(开发环境)[通俗易懂]

    vue 解决跨域问题(开发环境)[通俗易懂]一、什么是跨域问题同源:域名,协议,端口均相同不同源就是跨域,比如你的前端为localhost:9528,后端为localhost:8080,此时前端去访问后端接口就会产生跨域问题,因为端口不同。这里不详细讲,可参考百度百科-同源策略。二、如何解决跨域问题前后端都可以解决,这里仅提供vue的前端跨域解决方案:前端vue.config.js配置如下,然后保证在你访问后端接口时加个前缀/api。devServer:{port:port,open:false,ov

    2022年9月17日
    0
  • 爬虫框架Scrapy安装

    爬虫框架Scrapy安装免责声明:本文所记录的技术手段及实现过程,仅作为爬虫技术学习使用,不对任何人完全或部分地依据本文的全部或部分内容从事的任何事情和因其任何作为或不作为造成的后果承担任何责任。Scrapy爬虫框架用Python编写的功能强大,应用范围最广,最流行的爬虫框架,框架提供了大量的爬虫相关的组件,能够方便快捷的完成各网站的爬取。01Scrapy安装打开scrapy官方网站【scrapy.org】,目前最新版本为2.5:按照官方提供的脚本,在命令行执行安装:pipinst..

    2022年9月18日
    0
  • 设备驱动基础学习–platform driver简单实现「建议收藏」

    设备驱动基础学习–platform driver简单实现「建议收藏」设备驱动基础学习–platformdriver简单实现

    2022年7月4日
    20
  • 紫光同创国产FPGA学习之Power Calculator

    紫光同创国产FPGA学习之Power Calculator紫光同创里面的,芯片功耗计算。没看过,没用过。有兴趣瞄一下。我又不用再电池行业,对电源没啥要求。没用经验之谈,拷贝参考书。一、总体介绍  (一)PangoPowerCalculator总体介绍PangoPowerCalculator是用来计算用户设计产生功耗的软件,简称PPC,是集成在PDS中的一个组件。用户在完成design设计,经过综合、map、布局布线后,可以使用P…

    2022年8月30日
    0
  • 小试 ScriptManager

    小试 ScriptManager1概述ScriptManager控件管理用于MicrosoftASP.NETAJAX页面的客户端脚本。默认情况下,ScriptManager控件将MicrosoftAJAX库的脚本与页面注册到一起,这使脚本可以使用类型系统扩展并支持局部页面输出和Web服务调用。在页面中,必须使用ScriptManager控件来使下列MicrosoftASP.NETAJAX…

    2022年7月13日
    13

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号