Linux下动态库的使用


【简介】

linux环境下的动态库一般名为libxxx.so, 用ldd命令分析某个可执行程序,可以看到该程序依赖哪些动态库,以及路径。 如 ldd ./test

  linux-vdso.so.1 =>  (0x00007fffaab52000)

  libc.so.6 => /lib64/libc.so.6 (0x0000003c4c800000)

  /lib64/ld-linux-x86-64.so.2 (0x0000003c4c000000)

如果有依赖库找不到,程序会无法正常运行。

 

【创建一个动态库】

util.cpp

1
2
3
4
extern  "C"  int  InsertSubStr( char * pszBuf,  int  nPos,  char * pValue)
{
      return  1;
}

代码为cpp时,函数可以重载,在symbol里生成的函数名会带上修饰,如_ZwqInsertSubStrBstl. 加上extern "C"之后在symbol里就是原名,当然也就不能有同名函数了。如果函数体使用了该修饰后,头文件定义也必须加上,否则调用者编译时会找不到函数名。

makefile

1
2
3
4
5
libmyutil.so : util.o
   g++ -shared -o libmyutil.so util.o
 
util.o : util.cpp
   g++ -fPIC -c util.cpp -o util.o

 如果没有编译错误,则会生成libmyutil.so文件。因为使用了-shared编译选项,如果在代码里引用了其他库libabc.so,在编译时不会报错(不像windows下那样会报unresolve external,或者我还没找到设置方法)。如果调用者也没有引用该库libabc.so,则加载或运行时会报错。所以知道引用了哪些库,最好在makefile里加上,如g++ -shared -labc -o libmyutil.so util.o

QT创建动态库时,会生成libxx.so.1.0.0等几个带版本号的符号链接,可以加上 CONFIG += plugin 这样就不带版本号了。

 

【使用动态库】

像windows一样,也有直接编译链接和动态加载两种方式

1.直接编译链接

include头文件的函数定义后,在程序中调用函数,再在makefile中加上-lmyutil即可。用ldd命令分析执行程序,可以看到引用了libmyutil.so QT编译时,需要在pro文件里指定路径 LIB += -L../lib -lmymodule

2.动态加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <dlfcn.h>
 
typedef  int  (*fnTestFoo)( char * pszBuf,  int  nPos,  char * pValue);
 
   void  *hso = dlopen( "./libmyutil.so" , RTLD_NOW); //RTLD_LAZY);//
   if (!hso)
   {
     printf ( "dlopen failed:%s\n" , dlerror());
     return ;
   }
   fnTestFoo fnTest = (fnTestFoo)dlsym(hso,  "InsertSubStr" );
   char  *perr = dlerror();
   if (perr)
   {
     printf ( "load symbol failed:%s\n" , perr);
     return ;
   }
   char  szText[256]= "abcdef" ;
   fnTestFoo(szText, 2,  "xyz" );
   printf ( "%s\n" , szText);
   dlclose(hso);

可以看出与windows函数的对应:dlopen=LoadLibrary,dlsym=GetProcAddress. 这几个函数在libdl.so中,编译时在makefile中加上-ldl dlopen

可以有选项RTLD_NOW/RTLD_LAZY,前者为加载时解析依赖关系,失败则报错停止运行;后者为先不解析,运行到相应的代码时,如果有依赖关系错误再报错。

 

动态加载C++类

如果想要使用动态库中的C++类,直接使用肯定不行,因为编译时会找不到构造函数。其实只要在动态库中输出一个函数,创建类对象

CTest *GetClass_DL(void)

{

    return (new CTest());

}

这样就可以了,析构函数也一样。

 

【依赖关系和路径】

在windows下可以用depends来查看库的依赖关系,在Linux下有好几个工具可以达到类似效果,如上面提到的文件依赖关系的ldd. 如果要查看函数依赖关系,可以用nm命令,如nm test

1
2
3
4
5
6
7
8
0000000000601170 d _DYNAMIC
                  w _Jv_RegisterClasses
00000000004008e4 T _Z8callFunciPPcPFiS_zE
                  U dlclose@@GLIBC_2.2.5
                  U dlerror@@GLIBC_2.2.5
                  U dlopen@@GLIBC_2.2.5
                  U dlsym@@GLIBC_2.2.5
0000000000400da2 T main

其中T(text)前面是在本程序中的地址;U(unresolved)表示引用的外部库函数,以及对应的库名称。另外还有w(weak),A(absolute)不了解。 另外还有objdump命令,更详细的显示程序内的内容。

动态加载dlopen时,最好是指定全路径,否则当前路径可能发生变化。当编译链接库文件时,有时运行程序会找不到库文件报错:error while loading shared libraries: libxxx.so.1: cannot open shared object file: No such file or directory.很多时候是未指定路径。

与windows不同,把dll放在exe一起就行了,加载时会先从当前路径查找。linux下的库文件的路径配置在/etc/ld.so.conf 中,内容如下一行

include ld.so.conf.d/*.conf

在conf.d目录,可以看到其他软件所需的库文件路径,如mysql-x86_64.conf 里面也只有一行:/usr/lib64/mysql

所以如果自己的库文件不放在系统的库路径如/usr/lib64下的话,也可以照此方法增加myapp.conf文件,写上/mylib, 放在这里即可。

sudo ldconfig更新配置(注意较慢),用ldconfig -p|grep libmyxxx可以查看自己的库是否在系统的路径里。当在路径里新增so文件时,配置不会自动更新。

 

如果不想更改系统的库文件路径,也可以在运行时修改环境变量LD_LIBRARY_PATH,如 $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_library_path $ ./my_app

 

原文: https://www.cnblogs.com/chaos77/p/6874378.html

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM