基本介紹
- 外文名:dlopen
- 功能:打開一個動態程式庫
- 包含頭檔案:#include <dlfcn.h>
- 解析方式:RTLD_LAZY
介紹,基本定義,功能,包含頭檔案,函式定義,函式描述,流程,使用 dlopen,實例,
介紹
dlopen這個函式載入由以null結尾的字元串檔案名稱命名的動態共享對象(共享庫)檔案,並為載入的對象返回不透明的“句柄”。此句柄與 dlopen API 中的其他函式一起使用,例如dlsym(),dladdr(),dlinfo()和dlclose()。
如果 filename 為 NULL,則返回的句柄用於主程式。如果 filename 包含斜槓(“/”),則它被解釋為(相對或絕對)路徑名。否則,動態連結器將按如下方式搜尋對象(有關詳細信息,請參閱ld.so(8)):
- (僅限ELF)如果調用程式的執行檔包含 DT_RPATH 標記,並且不包含 DT_RUNPATH 標記,則會搜尋 DT_RPATH 標記中列出的目錄。
- 如果在程式啟動時,環境變數 LD_LIBRARY_PATH 被定義為包含以冒號分隔的目錄列表,則會搜尋這些目錄。 (作為安全措施,set-user-ID 和 set-group-ID程式將忽略此變數。)
- (僅限ELF)如果調用程式的執行檔包含 DT_RUNPATH 標記,則搜尋該標記中列出的目錄。
- 檢查快取檔案/etc/ld.so.cache(由ldconfig(8)維護)以查看它是否包含filename的條目。
- 搜尋目錄 /lib和 /usr/lib(按此順序)。
如果 filename 指定的對象依賴於其他共享對象,則動態連結器也會使用相同的規則自動載入這些對象。 (如果這些對象依次具有依賴性,則此過程可以遞歸地發生)
flags 參數必須包括以下兩個值中的一個:
- RTLD_LAZY執行延遲綁定。僅在執行引用它們的代碼時解析符號。如果從未引用該符號,則永遠不會解析它(只對函式引用執行延遲綁定;在載入共享對象時,對變數的引用總是立即綁定)。自 glibc 2.1.1,此標誌被LD_BIND_NOW環境變數的效果覆蓋。
- RTLD_NOW如果指定了此值,或者環境變數LD_BIND_NOW設定為非空字元串,則在dlopen()返回之前,將解析共享對象中的所有未定義符號。如果無法執行此操作,則會返回錯誤。
flags 也可以通過以下零或多個值進行或運算設定:
- RTLD_GLOBAL此共享對象定義的符號將可用於後續載入的共享對象的符號解析。
- RTLD_LOCAL這與RTLD_GLOBAL相反,如果未指定任何標誌,則為默認值。此共享對象中定義的符號不可用於解析後續載入的共享對象中的引用。
- RTLD_NODELETE (since glibc 2.2)在dlclose()期間不要卸載共享對象。因此,如果稍後使用dlopen()重新載入對象,則不會重新初始化對象的靜態變數。
- RTLD_NOLOAD (since glibc 2.2)不要載入共享對象。這可用於測試對象是否已經駐留(如果不是,則dlopen()返回 NULL,如果是駐留則返回對象的句柄)。此標誌還可用於提升已載入的共享對象上的標誌。例如,以前使用RTLD_LOCAL載入的共享對象可以使用RTLD_NOLOAD | RTLD_GLOBAL重新打開。
- RTLD_DEEPBIND (since glibc 2.3.4)將符號的查找範圍放在此共享對象的全局範圍之前。這意味著自包含對象將優先使用自己的符號,而不是全局符號,這些符號包含在已載入的對象中。
基本定義
功能
打開一個動態程式庫,並返回動態程式庫的句柄
包含頭檔案
#include <dlfcn.h>
函式定義
void * dlopen( const char * pathname, int mode);
函式描述
mode是打開方式,其值有多個,不同作業系統上實現的功能有所不同,在linux下,按功能可分為三類:
1、解析方式
RTLD_LAZY:在dlopen返回前,對於動態庫中的未定義的符號不執行解析(只對函式引用有效,對於變數引用總是立即解析)。
RTLD_NOW: 需要在dlopen返回前,解析出所有未定義符號,如果解析不出來,在dlopen會返回NULL,錯誤為:: undefined symbol: xxxx.......
2、作用範圍,可與解析方式通過“|”組合使用。
RTLD_GLOBAL:動態庫中定義的符號可被其後打開的其它庫解析。
RTLD_LOCAL: 與RTLD_GLOBAL作用相反,動態庫中定義的符號不能被其後打開的其它庫重定位。如果沒有指明是RTLD_GLOBAL還是RTLD_LOCAL,則預設為RTLD_LOCAL。
3、作用方式
RTLD_NODELETE: 在dlclose()期間不卸載庫,並且在以後使用dlopen()重新載入庫時不初始化庫中的靜態變數。這個flag不是POSIX-2001標準。
RTLD_NOLOAD: 不載入庫。可用於測試庫是否已載入(dlopen()返回NULL說明未載入,否則說明已載入),也可用於改變已載入庫的flag,如:先前載入庫的flag為RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)後flag將變成RTLD_GLOBAL。這個flag不是POSIX-2001標準。
RTLD_DEEPBIND:在搜尋全局符號前先搜尋庫內的符號,避免同名符號的衝突。這個flag不是POSIX-2001標準。
返回值:
打開錯誤返回NULL
成功,返回庫引用
編譯時候要加入 -ldl (指定dl庫) -rdynamic(通知連結器將所有符號添加到動態符號表中(目的是能夠通過使用 dlopen 來實現向後跟蹤)
例如
gcc test.c -o test -ldl-rdynamic
流程
打開庫:void* dlopen(const char* libfile,int flag);
取函式:void* dlsym(void* handler, const char* symbol);
運行函式:func
關閉庫:int dlclose(void* handler);
使用 dlopen
dlopen()是一個強大的庫函式。該函式將打開一個新庫,並把它裝入記憶體。該函式主要用來載入庫中的符號,這些符號在編譯的時候是不知道的。比如 Apache Web 伺服器利用這個函式在運行過程中載入模組,這為它提供了額外的能力。一個配置檔案控制了載入模組的過程。這種機制使得在系統中添加或者刪除一個模組時,都不需要重新編譯了。
可以在自己的程式中使用 dlopen()。dlopen() 在 dlfcn.h 中定義,並在 dl 庫中實現。它需要兩個參數:一個檔案名稱和一個標誌。檔案名稱可以是我們學習過的庫中的 soname。標誌指明是否立刻計算庫的依賴性。如果設定為 RTLD_NOW 的話,則立刻計算;如果設定的是 RTLD_LAZY,則在需要的時候才計算。另外,可以指定 RTLD_GLOBAL,它使得那些在以後才載入的庫可以獲得其中的符號。
實例
runlib.c
#include <stdio.h>#include <stdlib.h>#include <dlfcn.h>#define DLL_PATH "./libsd.so"typedef int (*func)(int, int);int main(){ void *dlhandler; char *error; func func = NULL; dlhandler = dlopen(DLL_PATH,RTLD_LAZY); if(dlhandler == NULL){ fprintf(stderr,"%s\n",dlerror()); exit(-1); } dlerror(); func = dlsym(dlhandler,"sumab"); printf("%d\n",func(1,2)); dlclose(dlhandler); return 0;}
編譯:
gcc -rdynamic -o runlib runlib.c -ldl
運行:
[teanee@localhost runlib]$ ./runlib3
成功調用sumab函式。