dlopen参数flag的使用

参考:https://blog.csdn.net/Bluenapa/article/details/119205993

使用dlopen接口动态加载共享库 ,函数原型如下

    void * dlopen( const char * pathname, int mode)

其第一个参数是包含so名称的路径,可以是相对路径;第二个参数mode是选项配置,可以结合实际场景需求配置,接下来认识下具体功能。

1、RTLD_NOW和RTLD_LAZY

  mode必须包含上述其中一个,表示解析方式,RTLD_NOW是调用dlopen后就解析所有函数符号,RTLD_LAZY是延时加载函数符号,使用的时候才解析,这样即使有一些未定义的函数符号,也能通过dlopen加载动态库。如下:main调用libA.so, 但是libA.so里面的函数里有未实现的函数,看看两个选项有啥区别。

  main.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
int main()
{
    // RTLD_NOW RTLD_LAZY
    void* handle = dlopen("./libA.so",RTLD_NOW);
    if (!handle)
       {
            printf("dlopen libA.so failed,err:%s\n",dlerror());
            return -1;
       }
    void (*func)(void)  = (void (*)(void))dlsym(handle,"funcA");
    if (!func)
       {
            printf("dlsym funcA failed, err: %s\n",dlerror());
            return -1;
       }
    func();
    return 0;
}

 A.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcB();
extern "C" void funcA()
{
funcB();
printf("this is funcA\n");
}

 编译命令:

g++ -fpic --shared A.cpp -o libA.so -ldl
g++ main.cpp -o main -ldl

  执行./main ,会在dlopen时就报错:  dlopen libA.so failed,err:./libA.so: undefined symbol: funcB

  如果将dlopen参赛mode改成RTLD_LAZY,就会发现调用dlopen和调用dlsym时都未报错,执行funcA时才报错:./main: symbol lookup error: ./libA.so: undefined symbol: funcB, 如果你不使用funcA,就不会出错

2、RTLD_LOCAL和RTLD_GLOBAL

  表示加载的函数符号的作用范围,RTLD_LOCAL与RTLD_GLOBAL作用相反,RTLD_GLOBAL 动态库中符号表全局打开,因此符号可被其后打开的其它库重定位;RTLD_LOCAL  动态库中符号表非全局打开,定义的符号不能被其后打开的其它库重定位。

  不填此选项,默认是RTLD_LOCAL

  如下:main加载libA.so,main加载libB.so并调用其函数funcB,而funcB调用A中funcA。(如果是mainmain加载libA.so,并调用funcA, libA.so动态加载libB.so, 并调用funcB,效果也是一样)

main.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>

int main()
{
    // RTLD_NOW RTLD_LAZY
    // RTLD_LOCAL和RTLD_GLOBAL
    void* handle = dlopen("./libA.so",RTLD_NOW|RTLD_LOCAL);
    if (!handle)
       {
            printf("dlopen libA.so failed,err:%s\n",dlerror());
            return -1;
       }
    void* handleB = dlopen("./libB.so",RTLD_NOW);
    if (!handleB)
       {
            printf("dlopen libB.so failed,err:%s\n",dlerror());
            return -1;
       }
    void (*func)(void)  = (void (*)(void))dlsym(handleB,"funcB");
    if (!func)
       {
            printf("dlsym funcB failed, err: %s\n",dlerror());
            return -1;
       }
    func();
    return 0;
}

A.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcC();
extern "C" void funcA()
{
    printf("this is funcA\n");
}

B.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcA();
extern "C" void funcB()
{
    printf("this is funcB\n");
    funcA();
}

编译命令:

g++ -fpic --shared A.cpp -o libA.so -ldl
g++ -fpic --shared B.cpp -o libB.so -ldl
g++ main.cpp -o main -ldl

执行./main.cpp, 会报错:dlopen libB.so failed,err:./libB.so: undefined symbol: funcA, 将dlopen libA.so的参数改成RTLD_GLOBAL,funcB就可以找到funcA符号,程序执行就没问题了。

3、RTLD_NODELETE、RTLD_NOLOA、DRTLD_DEEPBIND

RTLD_NODELETE表示在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量

RTLD_NOLOAD用于验证动态库是否已加载,dlopen()返回NULL表示未加载,否则已加载;也可用于改变已加载库的flag,如:先前加载库的flag为RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL

RTLD_DEEPBIND在规避同名符号冲突是很有用的,如果定义了此标志,在搜索全局符号时会优先搜索库内的符号,如下:main调用A里的funcA, funcA调用B里面的funcB函数,funcB调用funcC函数, libA.so和libB.so里面都有funcC函数

mian.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>

int main()
{
    // RTLD_NOW RTLD_LAZY
    // RTLD_LOCAL和RTLD_GLOBAL
    void* handle = dlopen("./libA.so",RTLD_NOW|RTLD_GLOBAL);
    if (!handle)
       {
            printf("dlopen libA.so failed,err:%s\n",dlerror());
            return -1;
       }
    void (*func)(void)  = (void (*)(void))dlsym(handle,"funcA");
    if (!func)
       {
            printf("dlsym funcA failed, err: %s\n",dlerror());
            return -1;
       }
    func();
    return 0;
}

A.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcC()
{
    printf("this is libA funcC\n");
}
extern "C" void funcA()
{
    printf("this is funcA\n");
    // RTLD_NOW RTLD_LAZY
    // RTLD_LOCAL和RTLD_GLOBAL
    //  RTLD_DEEPBIND
    void* handle = dlopen("./libB.so",RTLD_NOW|RTLD_GLOBAL|RTLD_DEEPBIND);
    if (!handle)
       {
            printf("dlopen libB.so failed,err:%s\n",dlerror());
            return;
       }
    void (*func)(void)  = (void (*)(void))dlsym(handle,"funcB");
    if (!func)
       {
            printf("dlsym funcB failed, err: %s\n",dlerror());
            return;
       }
    func();
}

B.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcC()
{
    printf("this is libB funcC\n");
}
extern "C" void funcB()
{
    printf("this is funcB\n");
    funcC();
}

编译命令:

g++ -fpic --shared B.cpp -o libB.so -ldl
g++ -fpic --shared A.cpp -o libA.so -ldl
g++ main.cpp -o main -ldl

A.cpp里面的dlopen使用了RTLD_DEEPBIND选项,输出打印是this is libB funcC, 如果去掉RTLD_DEEPBIND选项,输出打印是 this is libA funcC

 

  

 

     

posted @ 2023-05-01 20:23  ho966  阅读(763)  评论(0编辑  收藏  举报