爲了學習的順利進行,今天嘗試複習 C 語言和彙編語言的聯合編譯。代碼很簡單:函數
/* main.c */ #include <stdio.h> extern int Caculate(int nX, int nY); char strFormat[] = " %d + %d = %d\n"; int MyAdd(int nX, int nY); int main() { int nX = 3, nY = 2, nResult = 0; nResult = Caculate(nX, nY); printf(strFormat, nX, nY, nResult); return 0; } int MyAdd(int nX, int nY) { return nX + nY; }
; proc.nas extern strFormat extern MyAdd ; int MyAdd(int nX, int nY); extern printf global Caculate ; int Caculate(int nX, int nY); [SECTION .data] nResult dw 0 [SECTION .text] Caculate: push ebp ; 保存父函數的棧基址 mov ebp, esp ; 設置本函數的棧基址,即父函數的棧頂 sub esp, 0F0h ; 給子函數開闢棧空間 ; 取入口參數 mov ebx, [ebp + 12] ; 取參數2 mov eax, [ebp + 8] ; 取參數1 push ebx ; 參數入棧 push eax call MyAdd ; 函數的返回值保存在 eax 寄存器中 mov [nResult], eax ; 保存 _MyAdd 的返回值 add esp, 2 * 4 ; 兩個參數出棧 push eax mov ebx, [ebp + 12] mov eax, [ebp + 8] push ebx push eax push strFormat call printf add esp, 4 * 4 mov eax, [nResult] ; 設置本函數的返回值 leave ; 至關於下面兩條指令的效果 ; mov esp, ebp ; 恢復父函數的棧頂指針(= 本函數的棧基址) ; pop ebp ; 恢復父函數的棧基址 ret
#MakeFile ALL: nasm -felf32 proc.nas gcc -m32 -o main main.c proc.o
依然採用 GCC + NASM,能夠經過編譯,但是連接成執行文件怎麼都通不過,一直報錯「未定義函數」!
學習
嘗試各類操做系統(WinXP 32位、Win7 64位)、各類編譯器版本(MinGW 32 位、64 位)、各類編譯參數,搞了大半天,死活不讓我過!spa
就在快絕望的時候,忽然想起之前瀏覽的 C 語言庫函數,依稀記得函數名前面都帶有個下劃線 「_」!是否是就是這個緣由呢?試試,只需修改彙編代碼,涉及聯合編譯的變量和函數都加個下劃線前綴「_」。。。。。。操作系統
; proc.nas extern _strFormat extern _MyAdd ; int MyAdd(int nX, int nY); extern _printf global _Caculate ; int Caculate(int nX, int nY); [SECTION .data] nResult dw 0 [SECTION .text] _Caculate: push ebp ; 保存父函數的棧基址 mov ebp, esp ; 設置本函數的棧基址,即父函數的棧頂 ; 若是本函數有棧入口參數,則[bp + (n - 1) * 4]是第 n 個參數 sub esp, 0F0h ; 爲本函數的局部變量和調用中斷、子函數時保存現場數據的棧開闢空間 ; 取入口參數 mov ebx, [ebp + 12] ; 取參數2 mov eax, [ebp + 8] ; 取參數1 push ebx ; 參數入棧 push eax call _MyAdd ; 函數的返回值保存在 eax 寄存器中 mov [nResult], eax ; 保存 _MyAdd 的返回值 add esp, 2 * 4 ; 兩個參數出棧 push eax mov ebx, [ebp + 12] mov eax, [ebp + 8] push ebx push eax push _strFormat call _printf add esp, 4 * 4 mov eax, [nResult] ; 設置本函數的返回值 leave ; 至關於下面兩條指令的效果 ; mov esp, ebp ; 恢復父函數的棧頂指針(= 本函數的棧基址) ; pop ebp ; 恢復父函數的棧基址 ret
居然真是的,編譯經過了!指針
總結,彙編語言(NASM)和 C 語言(GCC)聯合編譯的時候(在 Windows 系統下),彙編代碼中和 C 語言產生混編關係的變量和函數,前面都要加一個下劃線「_」!C 語言代碼中不加。code