加载动态链接库——dlopen dlsym dlclose

DLOPEN?DLMOPEN?DLCLOSE

NAME

????dlclose, dlopen, dlmopen - 打开/关闭共享对象

SYNOPSIS

#include <dlfcn.h>

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

int dlclose(void *handle);

#define _GNU_SOURCE
#include <dlfcn.h>

void *dlmopen (Lmid_t lmid, const char *filename, int flags);

DESCRIPTION

dlopen()

????这个函数加载由以null结尾的字符串文件名命名的动态共享对象(共享库)文件,并为加载的对象返回不透明的“句柄”。此句柄与 dlopen API 中的其他函数一起使用,例如dlsym()dladdr()dlinfo()dlclose()

如果 filename 为 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)维护)以查看它是否包含filename的条目。
  • 搜索目录 /lib和 /usr/lib(按此顺序)。

????如果 filename 指定的对象依赖于其他共享对象,则动态链接器也会使用相同的规则自动加载这些对象。 (如果这些对象依次具有依赖性,则此过程可以递归地发生)

flags 参数必须包括以下两个值中的一个:

  • RTLD_LAZY
    执行延迟绑定。仅在执行引用它们的代码时解析符号。如果从未引用该符号,则永远不会解析它(只对函数引用执行延迟绑定;在加载共享对象时,对变量的引用总是立即绑定)。自 glibc 2.1.1,此标志被LD_BIND_NOW环境变量的效果覆盖。
  • RTLD_NOW
    如果指定了此值,或者环境变量LD_BIND_NOW设置为非空字符串,则在dlopen()返回之前,将解析共享对象中的所有未定义符号。如果无法执行此操作,则会返回错误。

flags 也可以通过以下零或多个值进行或运算设置:

  • RTLD_GLOBAL
    此共享对象定义的符号将可用于后续加载的共享对象的符号解析。
  • RTLD_LOCAL
    这与RTLD_GLOBAL相反,如果未指定任何标志,则为默认值。此共享对象中定义的符号不可用于解析后续加载的共享对象中的引用。
  • RTLD_NODELETE (since glibc 2.2)
    dlclose()期间不要卸载共享对象。因此,如果稍后使用dlopen()重新加载对象,则不会重新初始化对象的静态变量。
  • RTLD_NOLOAD (since glibc 2.2)
    不要加载共享对象。这可用于测试对象是否已经驻留(如果不是,则dlopen()返回 NULL,如果是驻留则返回对象的句柄)。此标志还可用于提升已加载的共享对象上的标志。例如,以前使用RTLD_LOCAL加载的共享对象可以使用RTLD_NOLOAD | RTLD_GLOBAL重新打开。
  • RTLD_DEEPBIND (since glibc 2.3.4)
    将符号的查找范围放在此共享对象的全局范围之前。这意味着自包含对象将优先使用自己的符号,而不是全局符号,这些符号包含在已加载的对象中。

dlmopen()

????这个函数除了以下几点与dlopen()有所不同外,都执行同样的任务。
????dlmopen()dlopen()的主要不同之处主要在于它接受另一个参数 lmid,它指定应该被加载的共享对象的链接映射列表(也称为命名空间)。对于命名空间,Lmid_t 是个不透明的句柄。
lmid 参数要么是已经存在的命名空间的ID(这个命名空间可以通过dlinfo RTLD_DI_LMID请求获得)或者是以下几个特殊值中的其中一个:

  • LM_ID_BASE
    在初始命名空间中加载共享对象(即应用程序的命名空间)。
  • LM_ID_NEWLM
    创建新的命名空间并在该命名空间中加载共享对象。该对象必须已正确链接到引用 所有其他需要的共享对象,因为新的命名空间最初为空。

如果 filename 是 NULL,那么 lmid 的值只能是LM_ID_BASE

dlclose()

????dlclose()减少指定句柄 handle 引用的动态加载共享对象的引用计数。如果引用计数减少为0,那么这个动态加载共享对象将被真正卸载。所有在dlopen()被调用的时候自动加载的共享对象将以相同的方式递归关闭。

????dlclose()成功返回并不保证与句柄相关的符号将从调用方的地址空间中删除。除了显式通过dlopen()调用产生的引用之外,一些共享对象作为依赖项可能已被隐式加载(和引用计数)。只有当所有引用都已被释放才可以从地址空间中删除共享对象。

RETURN VALUE

????执行成功时,dlopen()dlmopen()返回一个非空句柄。
????执行失败时(文件找不到、不可读、错误的格式或者在加载的时候出现错误),dlopen()dlmopen()返回 NULL。
????对于dlclose()成功执行,将返回0值,失败时,返回一个非0值。

以上这些函数产生的错误,其错误信息都可以通过dlerror()获知。

NOTES

dlmopen() 与 命名空间

????链接映射列表定义了通过动态链接器解析的符号的孤立命名空间。在命名空间内,被依赖的共享对象根据通常的规则被隐式加载,符号引用同样以通常的规则被解析。但是这种方案受限于已经被(显式和隐式)加载进命名空间的对象的定义。

????dlmopen()函数允许对象隔离加载——在新的命名空间中加载共享对象而不暴露其余的应用于新对象提供的符号。注意使用RTLD_LOCAL标志不足以达到此目的,因为它防止一个共享对象的符号对任何其他共享对象可用。在某些情况下,我们可能想使得由一些动态加载共享对象提供的符号对于其他共享对象可用,而不将这些符号暴露给整个应用。这可以通过使用单独的命名空间和RTLD_GLOBAL标志来实现。

????dlmopen()函数可以提供比RTLD_LOCAL标志更好的隔离效果。特别是,当共享对象是通过RTLD_LOCAL标志加载的,并且其依赖的共享对象是通过RTLD_GLOBAL加载的,那么有可能升级为RTLD_GLOBAL。因此,明确控制了所有共享对象的依赖的这种情况外,RTLD_LOCAL是不足以隔离加载的共享对象,。

????dlmopen()函数的一种用法是多次加载同样的对象。不使用dlmopen()函数来实现这个功能的话,需要创建共享对象的一个副本。而如果使用dlmopen()函数来实现的话,可以通过将相同的共享对象文件加载到不同的命名空间来实现。
glibc实现最多支持16个命名空间。

初始化和终结功能

????共享对象可以使用attribute ((constructor))attribute ((destructor))函数属性。构造函数在dlopen()返回之前执行,而析构函数在dlclose()返回之前执行。共享对象可以导出多个构造函数和析构函数并且优先顺序可以和每个函数相关联来决定它们的执行顺序。

DLSYM

NAME

????dlsym, dlvsym - 获取共享对象或可执行文件中符号的地址

SYNOPSIS

#include <dlfcn.h>

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

#define _GNU_SOURCE
#include <dlfcn.h>

void *dlvsym(void *handle, char *symbol, char *version);

DESCRIPTION

????dlsym()接受由dlopen()返回的动态加载的共享对象的“句柄”,并返回该符号加载到内存中的地址。如果未找到符号,则在加载该对象时,在指定对象或dlopen()自动加载的任何共享对象中,dlsym()将返回NULL。(dlsym()通过这些共享对象的依赖关系树进行宽度优先搜索。)
????因为符号本身可能是 NULL(所以dlsym()返回 NULL 并不意味着错误),因此判断是否错误的正确做法是调用dlerror()清除任何旧的错误条件,然后调用dlsym(),并且再次调用dlerror(),保存其返回值,判断这个保存的值是否是 NULL。
????可以在句柄中指定两个特殊的伪句柄:

  • RTLD_DEFAULT
    使用默认共享对象搜索顺序查找所需符号的第一个匹配项。搜索将包括可执行文件中的全局符号及其依赖项,以及使用RTLD_GLOBAL 标志动态加载的共享对象中的符号。
  • RTLD_NEXT
    在当前对象之后的搜索顺序中查找下一个所需符号。这允许人们在另一个共享对象中提供一个函数的包装器,因此,例如,预加载的共享对象中的函数定义(参见ld.so(8)中的LD_PRELOAD)可以找到并调用在另一个共享对象中提供的“真实”函数(或者就此而言,在存在多个预加载层的情况下,函数的“下一个”定义)。

????dlvsym()除了比dlsym()多提供了一个额外的参数外,其余与dlsym()相同。

RETURN VALUE

????执行成功,这些函数将会返回 symbol 关联的地址。执行失败,它们将返回 NULL。错误的原因可以通过dlerror()进行诊断。

EXAMPLE

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <gnu/lib-names.h>  /* Defines LIBM_SO (which will be a
                              string such as "libm.so.6") */
int
main(void)
{
    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 */

    cosine = (double (*)(double)) dlsym(handle, "cos");

    /* According to the ISO C standard, casting between function
      pointers and 'void *', as done above, produces undefined results.
      POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and
      proposed the following workaround:

          *(void **) (&cosine) = dlsym(handle, "cos");

      This (clumsy) cast conforms with the ISO C standard and will
      avoid any compiler warnings.

      The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.
      POSIX.1-2013) improved matters by requiring that conforming
      implementations support casting 'void *' to a function pointer.
      Nevertheless, some compilers (e.g., gcc with the '-pedantic'
      option) may complain about the cast used in this program. */

    error = dlerror();
    if (error != NULL) {
       fprintf(stderr, "%s\n", error);
       exit(EXIT_FAILURE);
    }

    printf("%f\n", (*cosine)(2.0));
    dlclose(handle);
    exit(EXIT_SUCCESS);
}

原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/9424930.html

时间: 2024-10-08 02:18:14

加载动态链接库——dlopen dlsym dlclose的相关文章

采用dlopen、dlsym、dlclose加载动态链接库【总结】

摘自http://www.cnblogs.com/Anker/p/3746802.html 采用dlopen.dlsym.dlclose加载动态链接库[总结] 1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统调用,非常方便.本文先从使用上进行总结,涉及到基本的操作方法,关于动态链接库的本质及如何加载进来,需要进一步学习,后续继续补充.如何将程序设

(十二)插件之dlopen/dlsym/dlclose 加载动态链接库

dlopen, dlsym, dlclose 加载动态链接库 参考: 采用dlopen.dlsym.dlclose加载动态链接库[总结] linux动态库加载的秘密 三种思路:解决动态库版本兼容 1. 插件 插件(Plug-in 又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序. 应用软件提供使插件能够应用的各项服务,其中包括提供加载方式,使插件可以加载到应用程序和网络传输协议中,从而和插件进行数据交换. 插件必须依赖于应用程序才能发挥自身功能,仅靠插件是无法正常运行的.相反地,应用程序

React-Native系列Android——SoLoader加载动态链接库

SoLoader是facebook出品的一款小巧的用于加载so库文件的开源项目,主要作用是自动检查和加载多个有依赖关系的so库文件.在Android平台下React-Native项目大量使用了动态链接库,即JNI技术,作为Java和Javascript两种程序语言之间的通信桥梁. 解压一个React-Native项目的安装包apk文件,我们可以看到一共有15个so库文件,其中libreactnativejni.so是JNI桥梁的入口. 而libreactnativejni.so又依赖于以下12个

【转】采用dlopen、dlsym、dlclose加载动态链接库【总结】

1.前言 为了使程序方便扩展,具备通用性,可以采用插件形式.采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件.linux提供了加载和处理动态链接库的系统调用,非常方便.本文先从使用上进行总结,涉及到基本的操作方法,关于动态链接库的本质及如何加载进来,需要进一步学习,后续继续补充.如何将程序设计为插件形式,挖掘出主题和业务之间的关系,需要进一步去学习. 2.生产动态链接库 编译参数 gcc -fPIC -shared  例如将如下程序编译为动态链接库

LINUX下动态链接库的使用-dlopen dlsym dlclose dlerror(转)

dlopen 基本定义 功能:打开一个动态链接库  包含头文件:  #include <dlfcn.h>  函数定义:  void * dlopen( const char * pathname, int mode );  函数描述:  在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程.使用dlclose()来卸载打开的库.  mode:分为这两种  RTLD_LAZY 暂缓决定,等有需要时再解出符号  RTLD_NOW 立即决定,返回前解除所有未决定的符号

C语言从代码中加载动态链接库

动态加载库需要用到的函数 函数:void *dlopen(const char *filename, int flag); 功能:打开动态链接库文件 参数:filename 动态链接库文件名 flag 打开方式,一般为RTLD_LASY 返回值:库指针 函数:char *dlerror(void); 功能:获取错误值 返回值:错误值 函数:void *dlsym(void *handle, const char *symbol); 功能:获取动态链接库中指定函数的指针 参数:handle 库指针

MFC加载动态链接库方法

1.LoadDll.cpp 1 #include "StdAfx.h" 2 #include "LoadDLL.h" 3 4 pMFCCallBackDll DLL_MFCCallBackDll; 5 6 HINSTANCE g_Hinstance; //实例句柄 7 8 //加载DLL 9 BOOL LoadDll(char *name) 10 { 11 //char name[200] = {""}; //DLL名字存放,且,最大长度为50

隐式加载动态链接库注意事项

在新建的Dll工程中,创建继承子类,其父类是另一Dll工程中的函数.此时,需将父类所在Dll工程的Lib文件加载到当前工程中,可能遇到的问题如下: 1.未将相关的头文件加到工程中,将导致加载的DLL没有相关函数的声明 2.编译时提示“can not open "***.lib" file”,是因为加载lib 库文件的路径没有设置正确,在,Project--Settings--Link--Category(Input选项)下的--Addtional Library Path中设置LIB文

(备忘)vs2010编写动态链接库时导出函数的函数名问题及加载方式

在vs2010中使用.def文件导出函数时,仅仅添加.def文件是不够的,还要在 项目属性 -> 链接器 -> 输入 -> 模块定义文件 中添加自定义的.def文件名. (前提:导入导出都在头文件和源文件中定义好了) ##:静态加载动态链接库 将链接库的 头文件..lib文件 和 .dll 文件拷贝到工程目录下 然后#include 头文件,#pragma comment(lib,"**.lib") 最后直接在需要使用dll函数的地方使用函数就行 ##:动态加载动态链