dlopen、dlsym和dlclose的使用


在dlopen()函數以指定模式打開指定的動態鏈接庫文件,並返回一個句柄給dlsym()的調用進程。使用dlclose()來卸載打開的庫。

dlopen:

dlopen()
The function dlopen() loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the dynamic
library. If filename is NULL, then the returned handle is for the main program. If filename contains a slash ("/"), then it is interpreted as a
(relative or absolute) pathname. Otherwise, the dynamic linker searches for the library as follows (see ld.so(8) for further details)

 

dlopen:功能:打開一個動態鏈接庫 
  包含頭文件: 
  #include <dlfcn.h> 
  函數定義: 
  void * dlopen( const char * pathname, int mode ); 

編譯時候要加入 -ldl (指定dl庫)

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標准。
 
dlsym:
 void*dlsym(void* handle,const char* symbol)
函數描述:
dlsym根據 動態鏈接庫操作句柄(handle)與符號(symbol),返回符號對應的地址。使 用這個函數不但可以獲取函數地址,也可以獲取變量地址
handle是由 dlopen打開 動態鏈接庫后返回的 指針,symbol就是要求獲取的函數或 全局變量的名稱。
返回值:void* 指向函數的地址,供調用使用。

dlclose()dlclose用於關閉指定句柄的動態鏈接庫,只有當此動態鏈接庫的使用計數為0時,才會真正被系統卸載。

dlerror()當動態鏈接庫操作函數執行失敗時,dlerror可以返回出錯信息,返回值為NULL時表示操作函數執行成功。

 

示例:

#include <stdio.h>
void hello(void)
{
    printf("hello\n");
}

編譯命令:

gcc -shared -o hello.so hello.c
代碼#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **argv)
{
    void *handle;
    void (*callfun)();
    char *error;
    handle = dlopen("/root/tmp/hello.so",RTLD_LAZY);  //如果hello.so不是在LD_LIBRARY_PATH所申明
                                                      //的路徑中必須使用全路徑名
    if(!handle)
    {
        printf("%s \n",dlerror());
        exit(1);
    }
    dlerror();
    callfun=dlsym(handle,"hello");
    if((error=dlerror())!=NULL)
    {
        printf("%s \n",error);
        exit(1);
    }
    callfun();
    dlclose(handle);
    return 0;
}

編譯命令:

 gcc -o hello_dlopen hello_dlopen.c -ldl

執行:./hello_dlopen

從中可以體會到編譯時不需要庫的好處。另外一種在編譯的時候需要動態庫的使用方法:

http://www.cnblogs.com/leaven/archive/2010/06/11/1756294.html

 示例2:

hello.c函數原型:
  

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

typedef struct {
 const char *module;
 int  (*GetValue)(char *pszVal);
 int   (*PrintfHello)();
} hello_ST_API;


int GetValue(char *pszVal)
{
 int retval = -1;
 
 if (pszVal)
  retval = sprintf(pszVal, "%s", "123456");
  printf("%s, %d, pszVer = %s\n", __FUNCTION__, __LINE__, pszVal);
 return retval;
}

int PrintfHello()
{
 int retval = -1;
 
 printf("%s, %d, hello everyone\n", __FUNCTION__, __LINE__);
 return 0;
}

const hello_ST_API  Hello = {
     .module = "hello",
   GetValue,
   PrintfHello,
};

 

編譯的時候用指令:

gcc -shared -o hello.so hello.c

上面的函數是用一個全局結構體hello來指向。在dlsym定義中說不僅可以獲取函數的地址,還可以獲取全局變量的地址。所以此處是想通過dlsym來獲取全局變量的地址。好處自己慢慢體會。

3、dlopen代碼

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

typedef struct {
 const char *module;
 int  (*GetValue)(char *pszVal);
 int   (*PrintfHello)();
} hello_ST_API;


int main(int argc, char **argv)
{
 hello_ST_API *hello;
 int i = 0;
 void *handle;
 char psValue[20] = {0};
 
 handle = dlopen(“庫存放的絕對路徑,你可以試試相對路徑是不行的", RTLD_LAZY);
 if (! handle) {
  printf("%s,%d, NULL == handle\n", __FUNCTION__, __LINE__);
  return -1;
 }
 dlerror();

 hello = dlsym(handle, "Hello");
 if (!hello) {
  printf("%s,%d, NULL == handle\n", __FUNCTION__, __LINE__);
  return -1;
 }

 if (hello && hello->PrintfHello)
  i = hello->PrintfHello();
  printf("%s, %d, i = %d\n", __FUNCTION__, __LINE__, i);
 if (hello && hello->GetValue)
  i = hello->GetValue(psValue);

 if (hello && hello->module)
  {
   printf("%s, %d, module = %s\n", __FUNCTION__, __LINE__, hello->module);
  }

    dlclose(handle);
    return 0;
}

 

編譯指令:gcc -o test hello_dlopen.c -ldl

 

運行./test結果如下。

PrintfHello, 27, hello everyone
main, 36, i = 0
GetValue, 19, pszVer = 123456
main, 42, module = hello

可以看到結果正常出來了。

 

看到沒用?dlsym找到全局結構體hello后,可以直接用這個全局結構體指針來使用庫里面的函數了,因為我們有時候提供的庫不僅僅是一個兩個函數的,一般的一個庫都會存在多個函數,用這種方式就可以直接使用了。不然找函數名稱的話要寫多少個dlsym啊?

 

參考:

http://www.cnblogs.com/leaven/archive/2011/01/28/1947180.html

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM