• dlopen、dlsym、dlclose加载动态链接库


    采用dlopen、dlsym、dlclose加载动态链接库

    转载请标注,熬夜写的文章,挺辛苦 ...

    环境

    系统: 16.04.1-Ubuntu
    编译器: gnu 5.4.0

    dlopen、dlsym及dlclose 基本使用

    // file : add.c
    int add(int a, int b) { return a+b; };
    
    // cmd: gcc -fPIC -shared -o libadd.so add.c
    // 编译生成动态库文件
    
    // file : demo.c
    #include <stdio.h>  
    #include <stdlib.h>   // EXIT_FAILURE
    #include <dlfcn.h>    // dlopen, dlerror, dlsym, dlclose
    
    typedef int(* FUNC_ADD)(int, int); // 定义函数指针类型的别名
    const char* dllPath = "./libadd.so";
    
    int main()
    {
        void* handle = dlopen( dllPath, RTLD_LAZY );
    
        if( !handle )
        {
            fprintf( stderr, "[%s](%d) dlopen get error: %s\n", __FILE__, __LINE__, dlerror() );
            exit( EXIT_FAILURE );
        }
    
        do{ // for resource handle
            FUNC_ADD add_func = (FUNC_ADD)dlsym( handle, "add" );
            printf( "1 add 2 is %d \n", add_func(1,2) );
        }while(0); // for resource handle
        dlclose( handle );
    }
    // cmd   : gcc -o demo demo.c -ldl; ./demo
    // output: 1 add 2 is 3
    

    C++ 的命名

    对于上述文件,采用 g++ 编译,会导致段错误如下:

    > g++ -fPIC -shared -g -o libadd.so add.c // -g 添加调试信息
    > g++ -g -o demo demo.c -ldl
    > ./demo
    
    段错误 (核心已转储)
    
    > ulimit -c unlimited // 设置 core 文件大小为无限制
    > ./demo 生成 core 文件
    > gdb ./demo core 调试段错误
    
    [New LWP 4396]
    Core was generated by `./demo'.
    Program terminated with signal SIGSEGV, Segmentation fault.
    #0  0x0000000000000000 in ?? ()
    
    > (gdb) break 19 // 设置断点在  FUNC_ADD add_func = (FUNC_ADD)dlsym( handle, "add" );
    > (gdb) print add_func
    
    $1 = (FUNC_ADD) 0x0  
    
    > (gdb) n
    20          printf( "1 add 2 is %d \n", add_func(1,2) );
    > (gdb) n
    
    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000000000 in ?? ()
    
    错误原因在于 dlsym 返回值为 0,通过该地址执行 add_func 导致段错误
    

    进一步分析原因,C++中尽管函数名称为 add 但是生成的 so 文件中的符号不是 add, 因为 c++ 要实现同名函数的重载,需要对函数命进行修饰,具体规则如下: C++函数名称修饰规则,可以通过查看符号表确认:

    > nm libadd.so
    0000000000000600 T _Z3addii
    > readelf -s libadd.so
        11: 0000000000000600    20 FUNC    GLOBAL DEFAULT    9 _Z3addii
    

    那么是不是可以通过 dlsym 打开符号 _Z3addii 来正确调用,尝试如下

    > vim demo.c
    
     19         // FUNC_ADD add_func = (FUNC_ADD)dlsym( handle, "add" );
     20         FUNC_ADD add_func = (FUNC_ADD)dlsym( handle, "_Z3addii" );
    
    > g++ -g -o demo demo.c; ./demo
    
    1 add 2 is 3
    
    > gdb demo
    > (gdb) break 21
    > (gdb) run
    > (gdb) print add_func
    $1 = (FUNC_ADD) 0x7ffff7607600 <add(int, int)>
    
    // ? 这里的地址与 so 的不一致,待深入分析
    

    最好的方法是通过 extern C 来处理

    // file : add.c
    #ifdef __cplusplus
    extern "C"{
    #endif
    
    int add(int a, int b) {return a+b; }
    
    #ifdef __cplusplus
    }
    #endif
    > g++ -fPIC -shared -g -o libadd.so add.c // -g 添加调试信息
    > nm libadd.so
    
    0000000000000600 T add
    

    尝试更改 add 的可见性

    // file : add.c
    #ifdef __cplusplus
    extern "C"{
    #endif
    
    static int add(int a, int b) {return a+b; }
    
    #ifdef __cplusplus
    }
    #endif
    > g++ -fPIC -shared -g -o libadd.so add.c // -g 添加调试信息
    > nm libadd.so
    
    00000000000005e0 t add
    
    > g++ -o demo demo.c -ldl; ./demo
    
    段错误 (核心已转储)

    参考:采用dlopen、dlsym、dlclose加载动态链接库 - 简书 (jianshu.com)

  • 相关阅读:
    【独家】K8S漏洞报告 | 近期bug fix解读
    idou老师教你学Istio 29:Envoy启动流程
    idou老师教你学Istio 28:istio-proxy check 的缓存
    idou老师教你学Istio :5分钟简析Istio异常检测
    idou老师教你学Istio 27:解读Mixer Report流程
    idou老师教你学Istio 26:如何使用Grafana进行可视化监控
    idou老师教你学Istio 25:如何用istio实现监控和日志采集
    idou老师教你学Istio 24:如何在Istio使用Prometheus进行监控
    idou老师教你学Istio 23 : 如何用 Istio 实现速率限制
    idou老师教你学Istio 22 : 如何用istio实现调用链跟踪
  • 原文地址:https://www.cnblogs.com/Malphite/p/16228600.html
Copyright © 2020-2023  润新知