本節(jié)是對前文的代碼說明,代碼再貼過來,C_with_S_c.c如下:
代碼C_with_S_c.c的第1行是聲明外部函數(shù)asm_print,通知編譯器這個函數(shù)并不在當(dāng)前文件中定義。我們知道它定義在文件C_with_S_S.S中,但編譯器是不知道的,所以只能在鏈接階段將此函數(shù)重新定位,編排地址。
第2~6行是c_print的實現(xiàn),在它的內(nèi)部,它又調(diào)用了匯編代碼C_with_S_S.S中的函數(shù)asm_print,經(jīng)過第1行的聲明,我們要給它提供兩個參數(shù):字符串的起始地址及長度。
特別強(qiáng)調(diào)一下,由于這里并不打算把c標(biāo)準(zhǔn)庫也鏈接進(jìn)來,所以在求字符串長度時,我們不能用string.h中的strlen函數(shù)。也就是說即使include <string.h>將其strlen的聲明加進(jìn)來,沒有strlen實現(xiàn)本身所在的.o文件也是不行的。函數(shù)聲明的作用是:一方面是告訴編譯器該函數(shù)的參數(shù)所需要的??臻g大小及返回值,這樣編譯器能為其準(zhǔn)備好執(zhí)行環(huán)境,另一方面是如果該函數(shù)是在外部文件中定義的,一定要在鏈接階段時將其對應(yīng)的目標(biāo)文件一塊鏈接進(jìn)來。所以這里第3~4行通過while循環(huán)求字符串的長度。字符串結(jié)尾必須是空字符’\0’才行,否則while就是死循環(huán)了。這個字符串是代碼C_with_S_S.S提供的,我們轉(zhuǎn)過去看看。
1 section .data 2 str: db 'asm_print says hello world!', 0xa, 0 3 ;0xa是換行符,0是手工加上的字符串結(jié)束符的ascii碼。 4 str_len equ $-str 5 6 section .text 7 extern c_print 8 global _start 9 _start: 10 ;;;;;;;;;;;; 調(diào)用c代碼中的函數(shù)c_print ;;;;;;;;;;; 11 push str ;傳入?yún)?shù) 12 call c_print ;調(diào)用c函數(shù) 13 add esp,4 ;回收??臻g 14 15 ;;;;;;;;;;;;;;;;;;; 退出程序 ;;;;;;;;;;;;;;;;;;;; 16 mov eax,1 ;第1號子功能是exit系統(tǒng)調(diào)用 17 int 0x80 ;發(fā)起中斷,通知linux完成請求的功能。 18 19 global asm_print ;相當(dāng)于asm_print(str,size) 20 asm_print: 21 push ebp ;備份ebp 22 mov ebp,esp 23 mov eax,4 ;第4號子功能是write系統(tǒng)調(diào)用 24 mov ebx, 1 ;此項固定為文件描述符1,標(biāo)準(zhǔn)輸出(stdout)指向屏幕 25 mov ecx, [ebp+8] ;第1個參數(shù) 26 mov edx, [ebp+12] ;第2個參數(shù) 27 int 0x80 ;發(fā)起中斷,通知linux完成請求的功能。 28 pop ebp ;恢復(fù)ebp 29 ret
在代碼C_with_S_S.S的第2行,定義待打印的字符串時,在結(jié)尾人為的加了個0,它就是空字符’\0’的ascii碼。
第8~9行是將_start導(dǎo)出為全局符號,為的是給鏈接器用的,之前解釋過了。
為了在匯編文件中引用外部的函數(shù)(未必是c代碼中的),需要用extern來聲明所需要的函數(shù)名。
由于我們用到了外部的函數(shù)c_print,所以在第7行用extern c_print來聲明c_print函數(shù)是外部的。第11~13行是為外部函數(shù)c_print壓棧傳參,調(diào)用它后清理棧空間。
第16~17行是調(diào)用exit系統(tǒng)調(diào)用告訴linux我要正常退出。
在匯編語言中導(dǎo)出符號名是用global關(guān)鍵字,這在之前說_start時大伙已有所耳聞,global是將符號導(dǎo)出為全局屬性,對程序中的所有文件可見,這樣其它外部文件中也可以引用被global導(dǎo)出的符號啦,無論該符號是函數(shù)還是變量。
由于在c代碼文件C_with_S_c.c中也調(diào)用了這里的asm_print函數(shù),所以為了讓外部代碼可以引用asm_print。我們在第19行用global asm_print將其導(dǎo)出。
第20行之后是asm_print的實現(xiàn),相信大家已經(jīng)非常明白了,不解釋。
好啦,通過這兩個例子我相信大伙兒已經(jīng)掌握c和匯編混合編程的方法啦,確切說是方法之一。對于這種匯編代碼和C代碼單獨編譯的方式還是較容易的,以后咱們會講C內(nèi)聯(lián)匯編的方式難為難為大家的,玩笑玩笑,只要用心沒有學(xué)不會的(聽上去似乎覺得更難了^_^,沒事沒事)。
有關(guān)混合編程的部分就說完了,總結(jié)一下:
在匯編代碼中導(dǎo)出符號供外部引用是用關(guān)鍵字global,引用外部文件的符號是用關(guān)鍵字extern。
在C代碼中只要將符號定義為全局便可以被外部引用(一般情況下無須用額外關(guān)鍵字修飾,具體請參考c語言手冊),引用外部符號時用extern聲明即可。