Re: [問題] DLL問題
早安,早起的鳥兒有蟲吃
繼續昨天的話題,也就是 DLL 的入口函數有沒有分 VC、BCB 版本。
已知 DLL 只是一個 PE/COFF 格式,與程式語言無關,可是 VC 的入口函數
叫我們要用 DllMain。而我不太熟的 BCB,根據原 PO 提供的資訊,其入口函數
卻是使用 DLLEntryPoint。分別觀察兩函數內容,可以發現差別只有名稱不同。
如果把 DllMain 與 DLLEntryPoint 一起暫稱為「Programmer 入口」,
而從 Windows 的 LoadLibrary() 函數,也就是 Loader 所進入的入口點,
稱為「DLL 入口」,則呼叫順序為:
LoadLibrary() --> 透過 DLL File Header 找到並調用「DLL 入口」 --> 透過
神祕機制找到並調用「Programmer 入口」。
其中的「DLL 入口」是由 VC, BCB 所提供,這兩家廠商的「DLL 入口」分別選用
不同名稱的「Programmer 入口」,才有今天的狀況。但這都不重要,對於使用
DLL 的用戶來說,反正啟動 DLL 的動作都是透過 LoadLibrary 完成,你在意的只
是後面要用到的 DLL 匯出函數,誰有空理 DllMain 或 DLLEntryPoint 呢?又沒
有程式設計師,需要直接調用這兩類入口函數。
===
做個實驗,順便示範如何用 VC 產生一個名稱不叫 DllMain 的「Programmer 入口」。
以下是呼叫 DLL 端的程式碼
----------------------------------------------------
#pragma comment(lib, "dllep")
int __declspec(dllexport) mysub(int, int);
int main() {
mysub(10, 3);
return 0;
}
----------------------------------------------------
以下為 DLL 的原始碼
----------------------------------------------------
/*
filename: dllep.c
compile with: cl.exe dllep.c /Zi /LD /link /entry:foo
The file header of dllep.dll contains a field: AddressOfEntryPoint.
In Visual C++ environment, this address will point to function
_DllMainCRTStartup(...) which is located in crtdll.c.
In _DllMainCRTStartup(...), there is a external function pointer:
extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID);
This pointer makes the DllMain(..) can be optional, since the final
address is filled by VC++ Linker.
The "DllMain" is just a default name. We could use alternative
one by linker option /entry.
*/
#include<windows.h>
#pragma comment(lib, "user32")
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
MessageBoxA(NULL, "DllMain(...) of dllep.dll is called.", "dll", MB_OK);
}
return 1;
}
BOOL WINAPI foo(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
MessageBoxA(NULL, "foo(...) of dllep.dll is called.", "dll", MB_OK);
}
return 1;
}
int __declspec(dllexport) mysub(int a, int b) {
return (a + b + 1024);
}
----------------------------------------------------
就是編譯時用 linker 選項 /entry:foo
來指定新的 DLL 入口名稱為 foo
並保留原始的 DllMain 函數,觀看執行 DLL 時,是否真的會使用 foo 當入口。
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 124.8.128.76
→
07/28 09:32, , 1F
07/28 09:32, 1F
→
07/28 10:23, , 2F
07/28 10:23, 2F
→
07/28 10:24, , 3F
07/28 10:24, 3F
→
07/28 10:24, , 4F
07/28 10:24, 4F
→
07/28 10:24, , 5F
07/28 10:24, 5F
如你所說,需要 GetProcAddress() 來取得匯出函數的位址,因為 DLL.dll 是
使用 LoadLibrary() 載入,是 explicit linking。
是原文哪段我的陳述有問題嗎?不懂為何提出此疑問
※ 編輯: purpose 來自: 124.8.137.220 (07/28 13:24)
→
07/28 13:56, , 6F
07/28 13:56, 6F
→
07/28 13:56, , 7F
07/28 13:56, 7F
你是說 mysub(10, 3); 這段吧
這裡就是用 implicit linking 的方式來載入 dllep.dll,所以不需要在原始碼中
明確使用 LoadLibrary 跟 GetProcAddress。
一開始編譯器無法 DLL 呼叫端的原始碼內找到 mysub 這個符號,所以會把其位址
隨便設,比如變成 call 0x0000 0000。
然後由 Linker 在產生 .exe 時,找出 mysub 符號來,因為此時其他 *.obj 跟 *.lib
裡,也不會有可能找到 mysub 符號,所以產生 dllep.dll 時,會附送一個
匯入用靜態函式庫,就是假的 dllep.lib。
Linker 透過 dllep.lib 找到 mysub 的位址,然後就能順利結連結出可以使用 xx.exe。
這裡的 pragma comment(lib, "dllep") 就是假指令,告訴 Linker 有 dllep.lib 這個
檔案的存在。
然後呢 Linker 從 dllep.lib 這件事得知,產生後的 xx.exe 在被人執行時,
需要相依於 dllep.dll,因此 Linker 還會在 xx.exe 檔案裡面,暗藏指示內容給
作業系統的 Loader。
當 Loader 載入 xx.exe 發現這些暗藏的指示時,就會自動把 dllep.dll 透
過 LoadLibrary() 函數去載入。
所以 xx.exe 的原始碼中,沒有也不必手動執行 LoadLibrary。
同理,這個 Loader 在順利載入 dllep.dll 成功後,還會自動更正 mysub 的位址。
該位址原本是來自 dllep.lib,沒有真正指到 dllep.dll。
此時會被 Loader 修正成相當於 GetProcAddresss() 所得到的位址。
所以 Loader 也把 GetProcAddress 省略掉了。
※ 編輯: purpose 來自: 124.8.137.220 (07/28 14:12)
※ 編輯: purpose 來自: 124.8.137.220 (07/28 14:24)
→
07/28 14:43, , 8F
07/28 14:43, 8F
→
07/28 14:43, , 9F
07/28 14:43, 9F
→
07/28 14:48, , 10F
07/28 14:48, 10F
→
07/28 14:49, , 11F
07/28 14:49, 11F
→
07/28 15:15, , 12F
07/28 15:15, 12F
→
07/28 15:15, , 13F
07/28 15:15, 13F
→
07/28 15:59, , 14F
07/28 15:59, 14F
推
07/28 20:48, , 15F
07/28 20:48, 15F
The #pragma directive is the method specified by the C standard for
providing additional information to the compiler.
1. 這是 C 語言標準規定的假指令,gcc, bcb, vc 都有在用
2. 提供額外資訊給 compiler 用以控制其行為用的,廣義來說 linker 也包含在內。
3. pragma 好像是來自 pragmatic? 隨便啦
4. 最常用的 pragma 指令就是 #pragma comment(lib, "xxx")
5. 微軟: http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
GNU: http://gcc.gnu.org/onlinedocs/gcc/Pragmas.html
※ 編輯: purpose 來自: 124.8.137.220 (07/28 21:06)
推
07/28 21:30, , 16F
07/28 21:30, 16F
推
07/29 17:24, , 17F
07/29 17:24, 17F
推
07/29 22:04, , 18F
07/29 22:04, 18F
討論串 (同標題文章)