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

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

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

makefile

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.动态加载

#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

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

 

posted @ 2017-05-18 17:10  chaos77  阅读(6468)  评论(0编辑  收藏  举报