現(xiàn)今,完全用匯編書寫的程序是非常少的。編譯器能很好地將高級語言轉(zhuǎn)換成有效的機(jī)器代碼。因為用高級語言書寫代碼非常容易,所以高級語言變得很流行。此外,高級語言比匯編語言更容易移植!
當(dāng)使用匯編語言時,我們經(jīng)常將它使用在代碼中的一小部分上。有兩種使用匯編語言的方法:在C中調(diào)用匯編子程序或內(nèi)嵌匯編。內(nèi)嵌匯編允許程序員把匯編語句直接放入到C代碼中。這樣是非常方便的;但是,內(nèi)嵌匯編同樣存在缺點。匯編語言的書寫格式必須是編譯器使用的格式。目前沒有一個編譯器支持NASM格式。不同的編譯器要求使用不同的格式。Borland和Microsoft要求使用MASM格式。DJGPP和Linux中g(shù)cc要求使用GAS格式。在PC機(jī)上,調(diào)用匯編子程序是更標(biāo)準(zhǔn)的技術(shù)。
在C中使用匯編程序通常是因為以下幾個原因:
1、需要直接訪問計算機(jī)的硬件特性,而用C語言很難或不可能做到。
2、程序執(zhí)行必須盡可能地快,而且相比于編譯器,程序員手動優(yōu)化的代碼更好。
最后一個原因不像它以前一樣有根據(jù)。因為這些年編譯器技術(shù)提高了,而且編譯器通常可以產(chǎn)生非常有效的代碼(特別是當(dāng)開啟編譯器優(yōu)化的時候)。調(diào)用匯編程序的缺點:可移植性和可讀性減弱了。
絕大部分的C調(diào)用約定已經(jīng)確定了。但是,還需要描述一些額外的特征。
保存寄存器
首先, C假定子程序保存了下面這幾個寄存器的值:EBX,ESI,EDI, EBP,CS,DS,SS,ES。這并不意味著不能在子程序內(nèi)部修改它們。相反,它表示如果子程序改變了它們的值,那么在子程序返回之前必須恢復(fù)它們的原始值。EBX,ESI和EDI的值不能被改變,因為C將這些寄存器用于寄存器變量。通常都是使用堆棧來保存這些寄存器的原始值。
函數(shù)名
大多數(shù)C編譯器都在函數(shù)名和全局或靜態(tài)變量前附加一個下劃線字符。例如,函數(shù)名f將指定為_f。因此,如果這是一個匯編程序,那么它必須標(biāo)記為_f,而不是f。Linux gcc編譯器并不附加任何字符。在可執(zhí)行的Linux ELF下,對于C函數(shù)f,你只需要簡單使用函數(shù)名f即可。但是,DJGPP的gcc卻附加了一個下劃線。注意,在匯編程序skeleton中(圖1.7),主程序函數(shù)名是_asm main。
傳遞參數(shù)
按照C調(diào)用約定,一個函數(shù)的參數(shù)將以一定順序壓入棧中,這個順序與它們出現(xiàn)在函數(shù)調(diào)用里的順序相反。考慮這條C語句:printf("x = %d\\n",x); 圖4.11展示了如何編譯這條語句(用等價的NASM格式)。圖4.12展示了執(zhí)行完printf函數(shù)的開始部分后,堆棧的狀態(tài)。printf函數(shù)一個可以攜帶任意個參數(shù)的C語言庫函數(shù)。C調(diào)用約定的規(guī)則就是專門為允許這些類型的函數(shù)而規(guī)定的。因為format字符串的地址最后壓入堆棧,所以不管有多少參數(shù)傳遞到函數(shù),
計算局部變量的地址
找到定義在data或bss段的變量的地址是非常容易的。基本上,連接程序做的就是這件事情。但是,要計算出在堆棧上的一個局部變量(或參數(shù))的地址就不簡單了。可是,當(dāng)調(diào)用子程序的時候,這種需求是非常普通的。考慮傳遞一個變量(讓我們稱它為x)的地址到一個函數(shù)(讓我們稱它為foo)的情況。如果x處在堆棧的EBP ? 8的位置,你不可以這樣使用:
mov eax, ebp - 8
為什么?因為指令MOV儲存到EAX里的值必須能由匯編器計算出來(也就是說,它最后必須是一個常量)。但是,有一條指令能做這種需求的計算。它就是LEA (即Load Effective Address,載入有效地址)。下面的代碼就能計算出x的地址并將它儲存到EAX中:
lea eax, [ebp - 8]
現(xiàn)在EAX中存有了x的地址,而且當(dāng)調(diào)用函數(shù)foo的時候,就可以將其壓入到棧中。不要搞混了,這條指令看起來是從[EBP-8]中讀數(shù)據(jù);然而,這并不正確。LEA指令永遠(yuǎn)不會從內(nèi)存中讀數(shù)據(jù)。它僅僅計算出一個將會被其它指令使用到的地址,然后將這個地址儲存到它的第一個操作數(shù)里。因為它并沒有實際讀內(nèi)存,所以不指定內(nèi)存大小(例如:dword)是必須的或說是允許的。
返回值
返回值不為空的C函數(shù)執(zhí)行完后會返回一個值。C調(diào)用約定規(guī)定了這個要如何去做。返回值需通過寄存器傳遞。所有的整形類型(char,int,enum,等)通過EAX寄存器返回。如果它們小于32位,那么儲存到EAX的時候,它們將被擴(kuò)展成32位。(它們?nèi)绾螖U(kuò)展取決于是有符號類型還是無符號類型。) 64位的值通過EDX:EAX寄存器對返回。浮點數(shù)儲存在數(shù)學(xué)協(xié)處理器中的ST0寄存器中。(這個寄存器將在浮點數(shù)這一章來討論。)
其它調(diào)用約定
所有的80x86 C編譯器中都支持上面描述的標(biāo)準(zhǔn)C調(diào)用約定的規(guī)則。通常編譯器也支持其它調(diào)用約定。當(dāng)與匯編語言進(jìn)行接口時,知道編譯器調(diào)用你的函數(shù)時使用的是什么調(diào)用約定是非常重要的。通常,缺省時,使用的是標(biāo)準(zhǔn)的調(diào)用約定;但是,并不總是這一種情況4。使用多種約定的編譯器通常都擁有可以用來改變?nèi)笔〖s定的命令行開關(guān)。它們同樣提供擴(kuò)展的C語法來為單個函數(shù)指定調(diào)用約定。但是,各個編譯器的這些擴(kuò)展標(biāo)準(zhǔn)可以是不一樣的。
GCC編譯器允許不同的調(diào)用約定。一個函數(shù)的調(diào)用約定可以通過擴(kuò)展語法attribute 明確指定。例如,要聲明一個返回值為空的函數(shù)f,它帶有一個int參數(shù),使用標(biāo)準(zhǔn)調(diào)用約定,需使用下面的語法來聲明它的原型:
void f ( int ) _attribute_(( cdecl ));
GCC同樣支持標(biāo)準(zhǔn)call 調(diào)用約定。通過把cdecl替換成stdcall,上面的函數(shù)可以指定為使用這種約定。stdcall約定和cdecl約定的不同點是stdcall要求子程序?qū)?shù)移除出棧(和Pascal調(diào)用約定一樣)。因此,stdcall調(diào)用約定只能使用在帶有固定參數(shù)的函數(shù)上(也就是說,不可以是函數(shù)printf和scanf)。
GCC同樣支持稱為regparm 的約定,這種約定告訴編譯器前3個整形參數(shù)通過寄存器傳遞給函數(shù),而不是通過堆棧。這是許多編譯器支持的一個共同的優(yōu)化模式。
Borland和Microsoft使用一樣語法來聲明調(diào)用約定。它們在C代碼中加上關(guān)鍵字_cdecl和_stdcall。這些關(guān)鍵字用來修飾函數(shù)。在原型聲明中,它們出現(xiàn)在函數(shù)名的前面例如,上面的函數(shù)f用Borland和Microsoft定義如下:
void _cdecl f ( int );
每種調(diào)用約定都有各自的優(yōu)缺點。cdecl調(diào)用約定的主要優(yōu)點是它非常簡單而且非常靈活。它可以用于任何類型的C函數(shù)和C編譯器。使用其它約定會限制子程序的可移植性。它的主要缺點是與其它約定相比它執(zhí)行較慢而且使用更多的內(nèi)存(因為函數(shù)的每次調(diào)用都需要用代碼將參數(shù)移除出
棧。)。
stdcall調(diào)用約定的主要優(yōu)點是相比于cdecl它使用較少的內(nèi)存。在CALL指令之后,不需要清理堆棧。它的主要缺點是它不能使用于可變參數(shù)的函數(shù)。
使用寄存器傳遞參數(shù)的調(diào)用約定的優(yōu)點是速度非常快。主要缺點是這種約定太復(fù)雜。有些參數(shù)可能在寄存器中,而另一些可能在堆棧中。
在匯編程序中調(diào)用C函數(shù)
C與匯編接口的一個主要優(yōu)點是允許匯編代碼訪問大型C庫和用戶寫的函數(shù)。例如,如果你想調(diào)用一下scanf函數(shù)來從鍵盤讀一個整形,該怎么辦?圖4.14展示了完成這件事的代碼。需要記住的非常重要的一點就是scanf函數(shù)遵循字面意義的C調(diào)用標(biāo)準(zhǔn)。這就意味著它保存了EBX,ESI和EDI寄存器的值;但是,EAX,ECX和EDX寄存器的值可能會被修改。事實上,EAX肯定會被修改,因為它將保存scanf調(diào)用的返回值。至于與C接口的其它例子,可以看用來產(chǎn)生asm io.obj的asm io.asm文件中的代碼。
-
PC機(jī)
+關(guān)注
關(guān)注
2文章
201瀏覽量
28666 -
匯編語言
+關(guān)注
關(guān)注
14文章
410瀏覽量
35962 -
編譯器
+關(guān)注
關(guān)注
1文章
1642瀏覽量
49292 -
nasm
+關(guān)注
關(guān)注
0文章
2瀏覽量
6874
發(fā)布評論請先 登錄
相關(guān)推薦
評論