關於VC中生成的PE(exe, dll, sys...)文件中對函數名稱的修飾

1.導出函數的方法:

(1)在要導出的函數簽名(signature)上添加關鍵字__declspec(dllexport)

例如:

void __declspec(dllexport) _cdecl someFun()

{

    printf("Hello, World!\n");

}


使用這種方法導出的函數,函數的名稱修飾,爲默認的修飾方法:

(1).1 若是從.c文件中導出則使用c語言的名稱修飾規則,其規則與函數的調用約定相關:

① _cdecl/__cdecl(c調用約定):要導出的函數名稱是someFun,名稱修飾後爲:someFun,也即是說,在該調用約定下,原函數名稱和修飾名稱是相同的不發生任何變化,包括大小寫!

② _stdcall/__stdcall(標準調用約定):要導出的函數名稱是someFun,名稱修飾後爲:_someFun@0,即其名稱修飾規則爲:在原函數名稱前面加一個下劃線(_),在原函數名稱後面加一個at符號(@),最後跟上該函數聲明的形式參數的類型所佔的字節數的總和。

③ _fastcall/__fastcall(寄存器傳參約定):要導出的函數名稱是someFun,名稱修飾後爲:@someFun@0,即其名稱修飾規則爲:在原函數名稱的前面和後面各加一個at符號(@),最後跟上該函數聲明的形式參數的類型所佔的字節數的總和。

(1).2 若是從.cpp文件中導出則使用c++語言的名稱修飾規則,其規則與函數的調用約定相關:其名稱修飾與函數的調用約定,函數參數類型,函數返回值類型,函數的作用域(全局函數,類的成員函數)等有關,具體參考MSDN。

提示:C++全局函數名稱修飾的快速識別方法:

① _cdecl/__cdecl: ?functionname@@YA......

② _stdcall/__stdcall: ?functionname@@YG......

③ _fastcall/__stdcall: ?functionname@@YI......

(1).3 編譯方法:

① cl /LD filename.c[pp] ------>filename.lib and filename.dll

② cl /c filename.c[pp] --->filename.obj

   link /dll filename.obj ---->filename.lib and filename.dll

(2)使用.def文件的方式導出函數

函數的簽名(signature)正常編寫,不需要加__declspec(dllexport),

例如:

void _cdecl someFun()

{

    printf("Hello, World!\n");

}

編寫好源文件後,還要編寫一個 .def 文件,其編寫方法如下:

LIBRARY Fun3

EXPORTS

    SomeFun1 = somefun1

    SomeFun2 =  somefun2@0

    SomeFun3 = @somefun3@0

將以上內容保存成一個.def文件,例如 mydll.def ,其中的 Fun3 是dll文件中的文件名,EXPORTS語句可以爲導出函數起別名,其方法如上所示:別名 = 函數的修飾名稱(也叫內部名稱),這種方法爲可以改名,從而使用DLL可以被其他語言編寫的程序使用(符號一致).

編譯方法:

① cl /LD filename.c[pp] /link /DEF:mydll.def

② cl /c filename.c[pp]

   link /DEF:mydll.def filename.obj ------>filename.dll and filename.lib

 

2. 使用從DLL文件中導出的函數的方法:

① 如果生成DLL文件時有函數簽名的頭文件( .h 文件),則將其 include 到我們的源文件中來(#include "filename.h"),這時就可以在源文件中直接使用.h中的導出函數名稱,在程序編譯好後,連接時要連接上dll對應的lib文件!!

② 沒有頭文件:使用以下聲明:

void __declspec(dllimport) _cdecl funcationname();

注意:函數的簽名要與函數的導出時的簽名完全一致,包括調用約定,static,const修飾等,同時還有加上__declspec(dllimport)關鍵字,在程序編譯好後,連接時要連接上dll對應的lib文件!!

③ 使用LoadLIbrary動態加載

 

3.關於extern "C"

因爲c語言與c++語言的Name Decoration規則不同,爲了使C語言編寫的DLL在c++程序中能使用,則需要在導出函數時加上extern "C",表明使用C的名稱修飾,這樣才能,正確找到符號,正確調用函數!

例如:

//filename.cpp

extern "C" void __declspec(dllexport) __cdecl somefun1()

{

    printf("Hello, World!\n");

}

注意:使用該函數時其,聲明爲void __declspec(dllimport) __cdecl somefun1();

不需要也不能加 extern "C" !!!!

 

4. PE中的函數符號問題

PE文件中的導入表中存放有該PE文件使用的所有外部符號(從其他DLL文件中導出的函數符號),就是經過名稱修飾的符號,具體符號形式由1中內容可知,DLL文件中的導出表中同樣存放有該DLL的導出的函數的符號,DLL加載器,就是根據這些符號來定位函數的信息(函數名稱,函數的虛擬地址)然後進行重定位。

 

5.COM(Component Object Module)中的 Create 型函數常常聲明爲extern "C"和__cdecl,這樣保證了函數在編譯成二進制文件時其名稱修飾後的函數名稱仍爲原函數名。實現了COM的二進制級別的語言無關性和實現與接口分離的特性!!

用dll來導出類,實際上就是導出類的方法而已.(使用名字修飾)

 

6.dll中導出數據

6.1到出普通數據

在dll中導出時,用__declspec(dllexport)修飾待導出的變量,在使用時使用__declspec(dllimport)來修飾該變量,例如:

//some.dll

__declspec(dllexport) int a = 0;

在客戶端使用時:要將生成some.dll時產生的導入庫文件soem.lib鏈接到客戶端程序中

__declspec(dllimport) int a;

6.2到導出共享數據(在dll中創建一個共享段)

#pragma data_seg(".shared")

__declspec(dllexport) int a = 0;

#pragma data_seg()

#pragma comment(linker, "/SECTION:.shared, RWS")

使用代碼在dll中建立了一個共享段,同時在該段中導出了一個共享變量a

客戶端可以使用__declspec(dllimport) int a;來使用該變量。

注意:在創建一個具有RWS屬性的段時一定要對變量進行初始化,否則,創建該段會失敗,編譯通不過。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章