推 lytn:太棒, 06/17 13:36
→ lytn:我還以為把 shell32.dll 加入參考就可以了,原來還有 linker 06/17 13:37
推 VictorTom:推一下z大:) 06/17 13:39
推 lytn:改完 可以 開 EXE 跟 TXT囉,大感謝阿,不然還不知道要拖多久 06/17 13:39
整理
Visual C++ 相對於 GNU 的 gcc 或 g++,命令列指令用的是 cl.exe,提供完整編譯
C, C++, C++/CLI 成目的檔 .obj 的編譯器功能,再加上連結成 .exe 的功能。
在 Visual C++ 選工具 -> VS 200X Command Prompt 叫出命令提示字元後
打 cl src.cpp /c 就會只做編譯器的功能輸出 src.obj。
而不加 /c 則會輸出 src.obj 跟 src.exe。
若指令 cl 加上參數 /MT,則編譯選項為 multithread support & static link
/MD 同上但 dynamic link,故/MD輸出的執行檔較小,但缺所需dll的電腦就不能跑。
/MTd 與 /MDd 為等於在 debug 模式編譯出結果,會附加偵錯資訊。
而執行 link src.obj 可連結出 src.exe 但似乎無法做 /MT /MD 的指定,比較不方便。
--
obj 檔是 COFF (Common Object File Format) 格式,可用 VC 附的指令 dumpbin 解讀
或上網抓把 dumpbin 包裝成 GUI 的 wumpbin 軟體。
dumpbin 查詢 src.obj 可發現加入 #pragma comment (lib,"shell32") 後
在 .drectve section 裡有個 Linker Directive 多出一行 /DEFAULTLIB:"shell32"。
因此不管用 cl.exe 或 link.exe 都會記得在連結 src.obj 時,順便加上 shell32.lib
一起連結成 exe。
--
obj 檔做 dumpbin:
.bss section 用來放沒有初始化的變數,如以下的 count
#include <stdio.h>
int count; //就算加了 static 使其變成內部連結,還是在.bss區
int data = 2;
int main(){
int i;
int j = 2;
return 0;
}
※ http://blog.linux.org.tw/~jserv/archives/002030.html
.data 放資料,通常是有初始化的全域變數,比如上面的 int data = 2;
你在 .data 區就會看到四個16進位的Byte寫 02 00 00 00
(因為CPU是little endian,所以不是 00 00 00 02)
就算是變成 static int data = 2; 其差別也只是變成內部連結,其他都一樣。
而上面的 i 跟 j 是當程式執行到函數 main 時才隨便從屬於堆疊的那塊記憶體
區域,找 4 個 Byte 決定當成 i 或 j,一旦此函數結束,函數裡的
auto 變數所佔的區域可能就變成另外一個 auto (local) 變數拿去用。
.rdata 放唯讀資料,比如 "C:\\123.txt" 這樣的字串常數。在程式執行後
如果寫入這個區域所在的記憶體,則 Linux 上應該會回報 Segment fault,
Windows 好像是跳出 Runtime error 視窗。
.text 除了資料以外的東西,應該就是指令。
symbol table 每個 obj 都有一份,有好幾筆,某筆可能寫
00000000 SECT3 External ?count@@3HA (int count)
告訴我們當初那個未初始化的全域變數 int count; 被放在 Section 3
此例也就是 .bss section,放在裡面偏移位置 00000000 的地方,
其實就是第一個 Byte 所在。
它是 external (外部連結)。因為它的宣告沒有加 static,
所以其他的 obj 檔只要透過 extern int count; 就能呼叫他。
而也因為是外部連結,所以 C++ 編譯器對 count 的符號名稱裝飾的
比較用心,變成比較複雜的 ?count@@3HA
如果當初是宣告成 static (內部連結),其他 obj 無法呼叫的話,
名稱就只會簡單的改成 _count 而已。
--
exe 檔是 PE 格式 (Portable Executable),Windows 32/64 位元用的可執行檔格式
Linux 是 ELF,DOS有個有名的com,寫組語的話會發現它很平易近人,只要用組合語言
寫完,把程式起始位址假定為 0x100 然後組譯,改成 .com 就已經是合格的執行檔。
沒有relocation顧慮也沒有header,因為點兩下 OS 就把它載入到 0x100 記憶體去,
就好像在寫 boot loader 可以直接假定BIOS會把你寫的程式載入到 0000:7c00 一樣。
exe 應該是改良自 COFF,總之用 dumpbin 就可以解讀。
打開exe後發現多出一個 Imports,裡面會寫這個執行檔需要配合哪個dll,而且
是用到這個dll的哪個函數。也可以用功能更強的 Dependcy Walker 來看。
通常都是程式寫完要拿到別台電腦執行,結果被VC專案設定預設的選項 /MD 錶了
不能跑後才想到要來看 Imports 了什麼 dll。
而那這 ShellExecute() 其實用 /MD 或 /MT 都一樣到最後還是要用到 SHELL32.dll,
而且編譯的時候一定要用到 lib,我猜 lib 裡面是定義用來呼叫dll的程式碼,不確定。
直接在 runtime 呼叫 dll 取得其函數所在,就可以躲過 Dependency Walker 等的偵測
當然 dll 還是必須存在才能執行其功能的。如下:
#include <windows.h>
//略
HINSTANCE hPDLL;
hPDLL = LoadLibrary(TEXT("shell32.dll"));
//呼叫 Windows API,故指定函數「呼叫慣例」為 __stdcall
typedef HINSTANCE (__stdcall *ptr)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, INT);
ptr ptrFun;
ptrFun = (ptr)GetProcAddress(hPDLL, "ShellExecuteA");
ptrFun(0, "edit", "C:\\windows\\winnt.bmp", 0, 0, SW_SHOWMAXIMIZED);
※ 編輯: zlw 來自: 124.8.131.252 (06/17 17:54)
→ zlw:打錯,不是 boot loader 是 boot sector 06/17 18:06