【簡介】
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