美文网首页C++程序员linux学习资料
采用dlopen、dlsym、dlclose加载动态链接库

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

作者: 呆呆的张先生 | 来源:发表于2018-10-12 00:27 被阅读9次

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

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

    环境

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

    参考

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

    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
    
    段错误 (核心已转储)
    

    python调用动态库(ctypes方式)

    Python调用C/C++动态链接库

    #! /usr/bin/python
    #-*- coding=utf-8 -*-
    
    import ctypes
    
    libadd = ctypes.cdll.LoadLibrary( ".//libadd.so" )
    print "1 add 2 is", libadd.add( 1, 2 )
    
    # chmod +x ./demo.py
    # ./demo.py
    
    1 add 2 is 3
    

    ctypes 的源码实现

    猜想是 dlopen 立 flags 睡觉

    相关文章

      网友评论

        本文标题:采用dlopen、dlsym、dlclose加载动态链接库

        本文链接:https://www.haomeiwen.com/subject/kdxwaftx.html