dlsym函數(shù)的功能就是可以從共享庫(kù)(動(dòng)態(tài)庫(kù))中獲取符號(hào)(全局變量與函數(shù)符號(hào))地址,通常用于獲取函數(shù)符號(hào)地址,這樣可用于對(duì)共享庫(kù)中函數(shù)的包裝;下面是函數(shù)原型及需要包含的頭文件。
#include <dlfcn.h>
void *dlsym(void *handle, const char *symbol);
其中handle可以是dlopen函數(shù)返回的handle值,也可以是RTLD_DEFAULT或RTLD_NEXT
RTLD_DEFAULT表示按默認(rèn)的順序搜索共享庫(kù)中符號(hào)symbol第一次出現(xiàn)的地址
RTLD_NEXT表示在當(dāng)前庫(kù)以后按默認(rèn)的順序搜索共享庫(kù)中符號(hào)symbol第一次出現(xiàn)的地址
具體區(qū)別可以通過(guò)下面的代碼dlsym1.c來(lái)區(qū)別:
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU? ? //使用RTLD_DEFAULT和RTLD_NEXT宏需定義
#include <dlfcn.h>
typedef size_t (*strlen_t)(const char *);
strlen_t strlen_f = NULL, strlen_f1 = NULL;
size_t strlen(const char *str)
{
? ? printf("%s strlen\n", __FILE__);
? ? return strlen_f1(str);
}
int main(int argc, char **argv)
{
? ? strlen_f = dlsym(RTLD_DEFAULT, "strlen");?
? ? //獲取到的是當(dāng)前文件中函數(shù)符號(hào)strlen的地址
? ? if(!strlen_f) {
? ? ? ? printf("default load error %s\n", dlerror());
? ? ? ? return 1;
? ? }?
? ? strlen_f1 = dlsym(RTLD_NEXT, "strlen");
? ? //獲取到的是當(dāng)前庫(kù)后的系統(tǒng)庫(kù)中函數(shù)符號(hào)strlen的地址
? ? if(!strlen_f1) {
? ? ? ? printf("next load error %s\n", dlerror());
? ? ? ? return 1;
? ? }?
? ? printf("strlen is %p\n", strlen);
? ? printf("strlen_f is %p\n", strlen_f);
? ? printf("strlen_f1 is %p\n", strlen_f1);
? ? printf("strlen_f is %ld\n", strlen_f("xuedaon")); //調(diào)用當(dāng)前文件中的函數(shù)strlen
? ? printf("=>>>>>>>>>> <<<<<<<<<<<=\n");
? ? printf("strlen_f1 is %ld\n", strlen_f1("xuedaon"));? //相當(dāng)于調(diào)用系統(tǒng)庫(kù)函數(shù)strlen
? ? return 0;
}
編譯當(dāng)前文件dlsym1.c
gcc? dlsym1.c? -ldl
運(yùn)行結(jié)果如下:

dlsym函數(shù)還可以獲取指定庫(kù)中的函數(shù)或全局變量符號(hào),需要先調(diào)用dlopen先打開我們指定的動(dòng)態(tài)庫(kù)文件,才能獲取符號(hào)地址,具體操作代碼事例:
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
int main(int argc, char **argv)
{
? ? // libhello.so是我們自己封裝的一個(gè)測(cè)試的共享庫(kù)文件
? ? // RTLD_LAZY 表示在對(duì)符號(hào)引用時(shí)才解析符號(hào),但只對(duì)函數(shù)符號(hào)引用有效,而對(duì)于變量符號(hào)的引用總是在加載該動(dòng)態(tài)庫(kù)的時(shí)候立即綁定解析
? ? void *handle = dlopen("./libhello.so", RTLD_LAZY);?
? ? if(!handle) {
? ? ? ? printf("open failed: %s\n", dlerror());
? ? ? ? return 1;
? ? }?
? ? void *p = dlsym(handle, argv[2]);? //argv[2]對(duì)應(yīng)輸入需獲取地址的符號(hào)名
? ? if(!p) {
? ? ? ? printf("load failed: %s\n", dlerror());
? ? ? ? return 1;
? ? }?
? ? // argv[1]對(duì)應(yīng)輸入0表示獲取的是全局變量的符號(hào)
? ? // argv[1]對(duì)應(yīng)輸入1表示獲取的是全局函數(shù)的符號(hào)
? ? if(0 == atoi(argv[1])) {
? ? ? ? printf("global is %d\n", *(int*)p);
? ? }else if(1 == atoi(argv[1])) {
? ? ? ? void (*fp)() = (void (*)())p;
? ? ? ? fp();
? ? }?
? ? dlclose(handle);
? ? return 0;
}

這里的libhello.so庫(kù)是通過(guò)下面的測(cè)試代碼源文件hello.c封裝的,具體內(nèi)容如下:
#include "hello.h"
int global = 666;
void hello()
{
? ? printf("hello xuedaon\n");
}
編譯上面的dlsym.c文件運(yùn)行效果如下:?

運(yùn)行時(shí)傳遞0表示獲取共享庫(kù)中全局變量global符號(hào)的地址,傳1 表示獲取共享庫(kù)中函數(shù)符號(hào)的地址。
注意在上述使用均是用的C編譯器實(shí)現(xiàn),如果是在C++編譯環(huán)境需使用dlsym這些函數(shù)需要注意的是由于C++的重載機(jī)制導(dǎo)致函數(shù)符號(hào)在編譯階段會(huì)將函數(shù)名重新改編,所以在使用時(shí)需要使用extern "C"告訴編譯器按C的編譯方式處理對(duì)應(yīng)的變量或?qū)ο蟆O旅媸莾煞N編譯時(shí)生成符號(hào)的區(qū)別:

而如果我們將hello.h中加上extern "C"修飾后我們會(huì)發(fā)現(xiàn)使用gcc與g++編譯時(shí)被extern "C"修飾的函數(shù)不會(huì)進(jìn)行函數(shù)名的重新改編。
hello.h
#pragma once
#include <stdio.h>
extern "C" {
? ? extern int global;
? ? extern void hello();
}
順便提一下大家如果想使用dlsym函數(shù)獲取類對(duì)象,那么可利用extern "C"配合函數(shù)接口返回類對(duì)象的指針或引用來(lái)實(shí)現(xiàn)。
另外用dlsym函數(shù)還可實(shí)現(xiàn)對(duì)庫(kù)函數(shù)malloc與free的包裝來(lái)檢測(cè)我們的代碼是否存在內(nèi)存泄漏(malloc與free若不成對(duì)則存在內(nèi)存泄漏),這里提供一個(gè)簡(jiǎn)單的實(shí)現(xiàn)思路代碼以供參考:
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
#define TEST_MEM_LEAK 1? ? //值為1表示加入內(nèi)存泄露的檢測(cè),為0表示不加入
#if TEST_MEM_LEAK
typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;
typedef void (*free_t)(void *p);
free_t free_f = NULL;
int malloc_flag = 1;? ? // 用于防止重復(fù)遞歸無(wú)法退出,因?yàn)閜rintf函數(shù)會(huì)調(diào)用malloc進(jìn)行內(nèi)存分配
int free_flag = 1;
void *malloc(size_t size)
{
? ? if(malloc_flag) {? ? ? ? ?
? ? ? ? malloc_flag = 0;? // 用于防止printf造成遞歸調(diào)用malloc而出錯(cuò)
? ? ? ? printf("malloc\n");
? void *p = malloc_f(size);
? ? ? ? malloc_flag = 1;? // 用于保證后續(xù)再次調(diào)用本文件中malloc時(shí)flag標(biāo)志的初始值一致
? return p;
? ? } else {
? ? ? ? return malloc_f(size);? // 這里調(diào)用dlsym獲取的系統(tǒng)庫(kù)中malloc函數(shù)
? ? }?
}
void free(void *p)
{
? ? if(free_flag) {
? ? ? ? free_flag = 0;
? ? ? ? printf("free\n");
? ? ? ? free_f(p);
? free_flag = 1;
? ? } else {
? ? ? ? free_f(p);
? ? }
}
#endif
int main(int argc, char **argv)
{
#if TEST_MEM_LEAK? ? // 這里if到endif之間的部分可分裝成函數(shù)調(diào)用
? ? malloc_f = dlsym(RTLD_NEXT, "malloc");
? ? if(!malloc_f) {
? ? ? ? printf("load malloc failed: %s\n", dlerror());
? ? ? ? return 1;
? ? }
? ? free_f = dlsym(RTLD_NEXT, "free");
? ? if(!free_f) {
? ? ? ? printf("load free failed: %s\n", dlerror());
? ? ? ? return 1;
? ? }
#endif
? ? void *p1 = malloc(10);? //這里會(huì)先調(diào)用本文中的malloc函數(shù)
? ? void *p2 = malloc(20);
? ? //這里的p2未釋放存在內(nèi)存泄漏,通過(guò)利用查看打印的malloc與free次數(shù)是否一樣來(lái)判斷
? ? free(p1);
? ? return 0;
}
編譯運(yùn)行效果如下:

這里可以看出調(diào)用了兩次malloc,而free只調(diào)用了一次,存在內(nèi)存泄漏。
以上就是dlsym的使用總結(jié),以供參考交流。文章來(lái)源:學(xué)到牛牛 www.xuedaon.com