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