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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 简单了解Activity工作流引擎

    简单了解Activity工作流引擎一、什么是工作流以请假为例,现在大多数公司的请假流程是这样的员工打电话(或网聊)向上级提出请假申请——上级口头同意——上级将请假记录下来——月底将请假记录上交公司——公司将请假录入电脑采用工作流技术的公司的请假流程是这样的员工使用账户登录系统——点击请假——上级登录系统点击允许就这样,一个请假流程就结束了有人会问,那上级不用向公司提交请假记录?公司不用将记录录入电脑?答案…

    2022年7月11日
    20
  • 服务器托管双线技术方案怎么写_自己搭建内网穿透服务器全端口

    服务器托管双线技术方案怎么写_自己搭建内网穿透服务器全端口多线路接入技术就是在互联网数据中心(IDC)通过特殊的技术手段把不同的网络接入商(ISP)服务接入到一台服务器上或服务器集群,使服务器所提供的网络服务访问用户能尽可能以同一个ISP或互访速度较快的ISP连接来进行访问,从而解决或者减轻跨ISP用户访问网站的缓慢延迟(南北网络瓶颈)问题。多线路接入是一个技术概念可以有多种具体实现方式,由于大多用户都是网通与电信,为了见简单起见,我们只讨…

    2022年10月23日
    0
  • Databus简介「建议收藏」

    Databus简介「建议收藏」1.背景在互联网架构中,数据系统通常分为真实数据(source-of-truth)系统,作为基础数据库,存储用户产生的写操作;以及衍生数据库或索引,提供读取和其他复杂查询操作。后者常常衍生自主数据存储,会对其中的数据做转换,有时还要包括复杂的业务逻辑处理。缓存中的数据也来自主数据存储,当主数据存储发生变化,缓存中的数据就需要刷新,或是转为无效。这样架构自然而然的一个问题就是如何保障基础数

    2022年10月17日
    1
  • Linux route add_linux系统route命令

    Linux route add_linux系统route命令routeadd命令的主要作用是加入静态路由,通常的格式是:routeADD157.0.0.0MASK255.0.0.0157.55.80.1METRIC3IF2參数含义:destinationmaskgatewaymetricinterface/*能够缩写*/destination【网段地址】mask【子网掩码】gateway【网关地址】…

    2022年8月12日
    2
  • 学习 Spring Boot 知识看这一篇就够了

    学习 Spring Boot 知识看这一篇就够了从2016年因为工作原因开始研究SpringBoot,先后写了很多关于SpringBoot的文章,发表在技术社区、我的博客和我的公号内。粗略的统计了一下总共的文章加起来大概有六十多篇了,其中一部分是在技术社区做的系列课程。我在写文章的时候将文章中的示例提取出来,作为开源代码分享了出来让大家以更方便的方式去学习(https://github.com/ityouknow/spri…

    2022年7月15日
    16
  • 基础SQL语句学习

    基础SQL语句学习最近老发牢骚,写了一些跟技术无关的东西,有点跑题了。以后还是注意多写技术性的东西。不知道有没有同学跟我一样,我一开始学sql语句的时候就觉得这个东西很无趣,不爱学,而且当时不知道从哪了解到数据库管理员都是一些年纪比较大的程序员在做。那时候觉得会WIN32,会编写算法,会设计模式很牛,都是一些看的见摸得着的东西,做起来很hight。反过来,操作数据库,这些都是别人给你做好了的,底层你都不了解

    2022年10月6日
    0

发表回复

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

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