linux平台 加载动态库dlsym返回null

46 篇文章 1 订阅

linux 平台加载动态库通常使用dlopen,dlsym,dlclose三个函数实现

最近写了一个小程序,遇到dlsym总是调用失败返回空值,查找了很多相关的资料,确定动态库的创建有问题、下面是最初版的 有问题的动态库代码:

dlldemo_global.h

#ifndef DLLDEMO_GLOBAL_H
#define DLLDEMO_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(DLLDEMO_LIBRARY)
#  define DLLDEMOSHARED_EXPORT Q_DECL_EXPORT
#else
#  define DLLDEMOSHARED_EXPORT Q_DECL_IMPORT
#endif


#endif // DLLDEMO_GLOBAL_H

dlldemo.h

#ifndef DLLDEMO_H
#define DLLDEMO_H

#include "dlldemo_global.h"




//导出函数
DLLDEMOSHARED_EXPORT  int   Add(int a,int b);
DLLDEMOSHARED_EXPORT  int   Sub(int a,int b);



class DLLDEMOSHARED_EXPORT DLLDemo
{

public:
    DLLDemo();

};

#endif // DLLDEMO_H

dlldemo.cpp

#include "dlldemo.h"


DLLDemo::DLLDemo()
{
}

int Add(int a,int b)
{
    return a+b;
}

int Sub(int a,int b)
{
    return a-b;
}

这样导出来的库中的函数已经发生了变化,可以通过nm libDLLDemo.so来查看

root@scada:/home/work/DLLDemo/lib# nm libDLLDemo.so
0000000000200b30 B __bss_start
0000000000200b30 b completed.6903
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000000660 t deregister_tm_clones
00000000000006f0 t __do_global_dtors_aux
0000000000200890 t __do_global_dtors_aux_fini_array_entry
0000000000200b28 d __dso_handle
00000000002008a0 d _DYNAMIC
0000000000200b30 D _edata
0000000000200b38 B _end
0000000000000794 T _fini
0000000000000730 t frame_dummy
0000000000200888 t __frame_dummy_init_array_entry
0000000000000880 r __FRAME_END__
0000000000200b00 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000000610 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000200898 d __JCR_END__
0000000000200898 d __JCR_LIST__
                 w _Jv_RegisterClasses
                 U qt_version_tag@@Qt_5.7
00000000000006a0 t register_tm_clones
0000000000200b30 d __TMC_END__
000000000000076b T _Z3Addii
000000000000077f T _Z3Subii
0000000000000760 T _ZN7DLLDemoC1Ev
0000000000000760 T _ZN7DLLDemoC2Ev
000000000000079d r _ZStL19piecewise_construct

发现Add函数已经变成了_ZNAddii,Sub函数变成了_ZNSubii,这样在动态引用时dlsym时总是返回null。下面是修饰规则:

DLL(动态库)导出函数名乱码含义  
C++编译时函数名修饰约定规则:    
  __stdcall调用约定:    
  1、以"?"标识函数名的开始,后跟函数名;   
  2、函数名后面以"@@YG"标识参数表的开始,后跟参数表;  
  3、参数表以代号表示:    
  X--void 
  D--char 
  E--unsigned char 
  F--short 
  H--int 
  I--unsigned int 
  J--long 
  K--unsigned long 
  M--float 
  N--double 
  _N--bool 
  ....    
  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
  4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;    
  5、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。    
  其格式为"[email protected]@YG*****@Z"或"[email protected]@YG*XZ",例如    
                      int Test1(char *var1, unsigned long)[email protected]@[email protected]

                     void Test2()-----"[email protected]@YGXXZ" 
  __cdecl调用约定:    
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。    
  __fastcall调用约定:    
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。  


 
  如果要用DEF文件输出一个"C++"类,则把要输出的数据和成员的修饰名都写入.def模块定义文件    
  所以...   通过def文件来导出C++类是很麻烦的,并且这个修饰名是不可避免的

怎么办呐?为什么呐?

这是因为在导出函数时使用的g++编译器,导出时自动加上约定的符号,为了使用原生的函数名,因此将导出函数名时加上extern “C”,表明使用gcc编译器,这样导出的函数名就不会改变了。以下是改过的头文件dlldemo.h代码:

#ifndef DLLDEMO_H
#define DLLDEMO_H

#include "dlldemo_global.h"




//导出函数
extern "C" DLLDEMOSHARED_EXPORT  int   Add(int a,int b);
extern "C" DLLDEMOSHARED_EXPORT  int   Sub(int a,int b);



class DLLDEMOSHARED_EXPORT DLLDemo
{

public:
    DLLDemo();

};

#endif // DLLDEMO_H

我们再来看看导出函数名吧:

root@scada:/home/work/DLLDemo/lib# nm libDLLDemo.so
000000000000076b T Add
0000000000200b30 B __bss_start
0000000000200b30 b completed.6903
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000000660 t deregister_tm_clones
00000000000006f0 t __do_global_dtors_aux
0000000000200890 t __do_global_dtors_aux_fini_array_entry
0000000000200b28 d __dso_handle
00000000002008a0 d _DYNAMIC
0000000000200b30 D _edata
0000000000200b38 B _end
0000000000000794 T _fini
0000000000000730 t frame_dummy
0000000000200888 t __frame_dummy_init_array_entry
0000000000000880 r __FRAME_END__
0000000000200b00 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000000608 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000200898 d __JCR_END__
0000000000200898 d __JCR_LIST__
                 w _Jv_RegisterClasses
                 U qt_version_tag@@Qt_5.7
00000000000006a0 t register_tm_clones
000000000000077f T Sub
0000000000200b30 d __TMC_END__
0000000000000760 T _ZN7DLLDemoC1Ev
0000000000000760 T _ZN7DLLDemoC2Ev
000000000000079d r _ZStL19piecewise_construct

以上命令输出结果查看到Add 和Sub两个函数名。再此调用函数dlsym,此时不为空,调用成功了。

以下是调用该动态库的代码:

#include <QCoreApplication>
#include <dlfcn.h>
//#include "dlldemo.h"

typedef int(* FuncAddTest)(int, int); // 定义函数指针类型的别名

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    const char *dylib_path = "/home/work/DLLDemo/lib/libDLLDemo.so";
    void *handle = NULL;
    char *error = NULL;
    if((handle = dlopen(dylib_path, RTLD_GLOBAL | RTLD_NOW)) == NULL)
    {
        printf("dlopen - %sn", dlerror());
        exit(-1);
    }
    else
    {
        //获取add地址
       FuncAddTest addfunc =  (FuncAddTest)dlsym(handle, "Add");
       if(addfunc)
       {
       printf("1 + 2 = %d\n",addfunc(1,2));       
       }
       else
       {
           if ((error = dlerror()) != NULL)  {
                      fprintf (stderr, "%s ", error);
                      exit(1);
                  }
           printf("未找到\n");
       }
       dlclose(handle);
    }
    return a.exec();
}

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 环境下动态加载库,可以使用动态链接库(Dynamic Linking)实现。动态链接库是一种可执行文件,它在运行时才会被加载到内存,并且可以在多个程序之间共享。这样,可以减小每个程序的内存占用,同时也方便了代码的维护和更新。 Linux 系统提供了一些函数来实现动态加载库,包括 `dlopen()`、`dlsym()`、`dlclose()` 等。其中,`dlopen()` 函数用于打开一个动态链接库,`dlsym()` 函数用于获取动态链接库中的符号,`dlclose()` 函数用于关闭动态链接库。 下面是一个简单的动态加载库的示例: ```c #include <stdio.h> #include <dlfcn.h> int main() { void *handle; double (*cosine)(double); char *error; handle = dlopen("libm.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); return 1; } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); return 1; } printf("%f\n", (*cosine)(2.0)); dlclose(handle); return 0; } ``` 上面的代码中,首先使用 `dlopen()` 函数打开了名为 `libm.so` 的动态链接库,并将其句柄保存在 `handle` 变量中。然后使用 `dlsym()` 函数获取了名为 `cos` 的符号,并将其保存在 `cosine` 变量中。最后,调用了 `cosine` 函数,并将结果打印出来。最后,使用 `dlclose()` 函数关闭了动态链接库。 需要注意的是,在使用动态链接库时,需要确保库文件的路径正确,并且需要保证库文件与应用程序的架构一致。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值