C與彙編的介面技術

來源:互聯網
上載者:User
1.儲存寄存器
  首先,C假定子程式儲存了下面這幾個寄存器的值:EBX,ESI,EDI,EBP,CS,DS,SS,ES。這並不意味著不能在子程式內部修改他們。相反,它表示如果子程式改變了它們的值,那麼子程式返回之前必須恢複它們的原始值。EBX,ESI和EDI的值不能被改變,因為C將這些寄存器用於寄存器變數。通常都是使用堆棧來儲存這些寄存器的原始值。

2.函數名
  大多數C編譯器都在函數名和全域或靜態變數前附加一個底線。例如,函數名f將指定為_f,而不是f。Linux gcc編譯器並不附加任何字元。在可執行檔Linux ELF下,對於C函數f,你只需要簡單使用函數名f即可。但是,DJGPP的gcc卻附加了一個底線。

3.傳遞參數
  按照C呼叫慣例,一個函數的參數將以一定順序壓入棧中,這個順序與他們出現在函數裡的順序相反。
  考慮這條C語句:printf("x=%d\n",x);上展示了如何編譯這條語句(用等價的NASM格式)。下展示了執行完printf函數的開始部分後,堆棧的狀態。printf函數是一個可以攜帶任意個參數的C語言庫函數。C呼叫慣例的規則就是專門為允許這些類型的函數而規定的。因為format字串最後壓入堆棧,所以不管有多少參數傳遞到函數,它在堆棧裡的位置將總是EBP+8。然後printf代碼就可以通過看format字串的位置來決定需要傳遞多少參數和堆棧上如何找到它們。
  當然,如果有錯誤發生,printf("x=%d\n"),printf代碼仍然會將[EBP+12]中的雙字輸出,而這並不是x的值。segment .data
x        dd    0
format   db    "x=%d\n",0

segment .text

    push dword [x]    ;將x的值壓入棧中
    push dword format ;將format字串的地址壓入棧中
    call _printf      ;注意底線!
    add esp,8         ;從棧中移除參數


4.計算局部變數的地址
  找到定義在data或bss段的變數地址是非常容易的。基本上,串連程式做的就是這件事情。但是,要計算出在堆棧上的一個局部變數(或參數)的地址就不簡單了。可是,當調用子程式時,這種需求是非常普通的。考慮傳遞一個變數(讓我們稱它為x)的地址到一個函數(讓我們稱它為foo)的情況。如果x處在堆棧的EBP-8的位置,你不可以這樣使用:
mov  eax,ebp-8
為什嗎?因為指令MOV儲存到EAX裡的值必須能有彙編器計算出來(也就是說,它最後必須是一個常量)。但是,有一條指令能做這種需求。它就是LEA(Load Effective Address)。下面的代碼就能計算出x的地址並將它儲存到EAX中:
lead  eax,[ebp-8]
現在EAX中有了x的地址,而且當調用函數foo的時候,就可以將其壓入到棧中。

5.傳回值
    傳回值不為空白的C函數執行完後會返回一個值。C呼叫慣例規定了這個要如何去做。傳回值需要通過寄存器傳遞。所有的整形類型(char,int,enum等)通過EAX寄存器返回。如果他們小於32位,那麼儲存到EAX的時候,他們將被擴充成32位。64位的值通過EDX:EAX寄存器對返回。浮點數出巡在數學副處理器的ST0寄存器中。

6.其它呼叫慣例
    所有的80x86 C編譯器中都支援上面描述的標準C呼叫慣例的規則。通常編譯器也支援其它呼叫慣例。擋雨組合語言進行介面時,知道編譯器調用你的函數時使用的是什麼呼叫慣例是非常重要的。通常,預設時,使用的是標準的呼叫慣例;但是,並不總是這一種情況。使用多種約定的編譯器都擁有可以用來改變預設約定的命令列開關。他們同樣提供擴充的C文法來為單個函數指定呼叫慣例。但是,各個編譯器的這些擴充標準可以是不一樣的。
    GCC編譯器允許不同的呼叫慣例。一個函數的呼叫慣例可以通過擴充文法__attribute__明確指定。例如,要聲明一個傳回值為空白的函數f,它帶有一個int參數,使用標準呼叫慣例,需要使用下面的文法來聲明它的圓形:
    void f(int)  __attribute__((cdecl));
GCC同樣支援標準call呼叫慣例。通過把cdecl替換成stdcall,上面的函數可以指定為使用這種約定。stdcall約定和cdecl約定的不同點是stdcall要求子程式將參數移除棧(和Pascal呼叫慣例一樣)。因此,stdcall呼叫慣例只能使用在帶有固定參數的函數上。
    Borland和Microsoft使用一樣文法來聲明呼叫慣例。他們在C代碼中加上關鍵字_cdecl和_stdcall。這些關鍵字來修飾函數。在原型聲明中,他們出現在函數名的前面:
       void _cdecl f(int);
    每種呼叫慣例都有自己的優缺點。cdecl呼叫慣例的主要有點是它非常簡單而且靈活。它可以用於任何類型的C函數和C編譯器。使用其它約定會限制子程式的可移植性。他的主要缺點是與其他約定相比它的執行較慢而且使用更多的記憶體。
    stdcall呼叫慣例的主要優點是相比於cdecl它使用較少的記憶體。在CALL指令之後,不需要清理堆棧。它的主要缺點是它不能用於可變參數的函數。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.