在C++编译器下dlsym()引发的思考

来源:互联网 发布:大数据征信查询入口 编辑:程序博客网 时间:2024/05/16 10:59
 

C++编译器下dlsym()引发的思考

cafesun 2007-02-16


这几天看到讲解dlopen,dlsym函数的文章,忍不住自己编码尝试了一下。引出了一些其他知识。


dlsym()的函数原型是

void* dlsym(void* handle,const char* symbol)


handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用。dlsym的返回值与symbol参数就是本文着重要讲述的要点。


先看下面一段代码



////////////////////////////////DLLTest.cpp//////////////////////////////////////////////////////

#include "DateTime.h"

#include <dlfcn.h>

#include <iostream>

using namespace std;


typedef int(*FuncDatePtr)(DateType* d);


int main(int argc,char* argv[])

{

DateType d;

//TimeType t;

void* dp=0;

char* error=0;

cout<<"Dll Programe Demo:"<<endl;

dp=dlopen("libtime.so",RTLD_NOW);

if(dp==0)

{

cout<<"Open time.so Failed!"<<dlerror()<<endl;

return 1;

}

//int(*f)(DateType* d);

FuncDatePtr f=(FuncDatePtr)dlsym(dp,"getdate2");

//void* test=dlsym(dp,"getdate");

//funcDate=(FuncDate)funcDate;

error=dlerror();

if(error)

{

cout<<"ERROR:"<<error<<endl;

return 1;

}

f(&d);

cout<<"Today Year="<<d.year<<endl;

dlclose(dp);

return 0;

}


//////////////////DateTime.h/////////////////////////

#ifndef DATETIME_H_

#define DATETIME_H_


#ifdef __cplusplus

extern "C"{

#endif


struct DateType

{

int year;

int month;

int day;

};


struct TimeType

{

int hour;

int minute;

int second;

};



int getdate2(DateType* d);


int gettime2(TimeType* t);


#ifdef __cplusplus

}

#endif



///////////////////////////////////////DateTime.cpp////////////////////////////////////////////////////

#include <time.h>

#include <iostream>

#include "DateTime.h"

using namespace std;



int getdate2(DateType* d)

{

long ti;

struct tm *tm;

time(&ti);

tm=localtime(&ti);

d->year=tm->tm_year+1900;

d->month=tm->tm_mon+1;

d->day=tm->tm_mday;

cout<<"function getdate() loaded!"<<endl;

return 0;

}


int gettime2(TimeType* t)

{

long ti;

struct tm *tm;

time(&ti);

tm=localtime(&ti);

t->hour=tm->tm_hour;

t->minute=tm->tm_min;

t->second=tm->tm_sec;

return 0;

}



DateTime.h,DateTime.cpp两个文件主要包含两个函数getdate2,gettime2(为什么函数名如此,在下面会专门提到)生成了动态链接库libtime.soDLLTest.cpp中的逻辑就是打开libtime.so这个文件,然后取getdate2函数,并调用它。


第一个要讲的就是 FuncDatePtr f=(FuncDatePtr)dlsym(dp,"getdate2");这个地方。看似简单的一个指针强制转换,实际上并不是那么简单,这里的指针转换不同于一般的指针转换,实际上是将一个指向对象的指针转换为指向函数的指针,这里除了使用传统的强制转换方式,还可以使用C++自带的reinterpret_cast转换符,可以这样写:

void* vf=dlsym(dp,"getdate2");

FuncDatePtr f=reinterpret_cast<FuncDatePtr>(vf);


这个转换只在C++中需要,因而也只在C++中遇到这个问题。



第二点要讲的是DateTime.h中的

#ifdef __cplusplus

extern "C"{

#endif


......


#ifdef __cplusplus

}

#endif


为什么一定要定义这样的宏呢,如果不定义会怎么样,也许很多人(包括我以前)也会这样问。既然有这样的疑问,那一切以实例说话,我去掉这个地方extern “C”的声明,看看编译的程序运行起来会怎么样......


稍作修改后,DllTest依然可以编译,无甚特别的。但运行一下呢?喔喔,怎么老是报错“Error:No Error”Error是我代码里面定义的输出,那个No Errordlerror返回的字符串。说是No Error,其实还是有错的,只是错不在dlsym(),而是错在我这里。


可以返回去重新看看那段代码

FuncDatePtr f=(FuncDatePtr)dlsym(dp,"getdate2");

如果你一眼看出问题了,那下面要讲的内容,你可以不看了。如果3分钟内没有发现问题,那么还是听我罗嗦一下。大家肯定听说过C+Name Mangling机制吧(没有听说的话,可以查看一下Lippman的《深入探索C++对象模型》)。当我去掉了extern “C”的时候,就注定会跌入这个陷阱。我用的g++编译的库文件,g++自然的运用了Name Mangling技术,原本的getdate(DateType& d)函数,很有可能其名字已变成了_getdate_DateType(DateType& d)这样(不同的编译器实现不一样),而对于引用dlsym这样的纯C编译好的库中的函数,他们对Name Mangling机制无甚了解,不会有智能的转换,所以它在C++编译器下不会自动的去作名字处理,当我传入getdate2作为symbol参数的时候,dlsym自然是找不到对应的函数地址的。但他自己又没啥错,就丢给我一串“No Error”


到了这里,了解没有extern “C”会发生什么事情,并且了解了原因后,我们可以转回来。extern “C”起了什么作用,大家心里应该都清楚了。我懒得引用官方正式的解释了,虽然定义的很完善,但我觉得很挠口,很难懂。所以我把我自己的理解帖出来,不怕大家见笑。


extern “C“就是告诉C++编译器,到了这里要用C编译器的方式来编译,不要什么Name Mangling之类的高科技了。细致的从语法上分析,有两层含义,但同样讲得也是这个意思。

  1. extern 关键字声明被修饰的对象可供其他模块调用(这里乱盖一下,可能被乱砖砸晕。我试着用GCC编译了一下,在C编译器下,如果函数被extern修饰,其他模块引用该函数,可以不包含该模块的头文件的)

  2. C”,告诉C++编译器,这里用C的标准编译。


好了,dlsym引出的问题一一解决了,这里提醒各位,在编译的时候,最好加上-ldl参数。至于makefile,我想大家都有(或者不需要makefile),编译的问题,我就不罗嗦了。Over








原创粉丝点击