內嵌彙編文法如下:
__asm__(彙編語句模板: 輸出部分: 輸入部分: 破壞描述部分)
共四個部分:彙編語句模板,輸出部分,輸入部分,破壞描述部分,各部分使用“:”格開,彙編語句模板必不可少,其他三部分可選,如果使用了後面的部分,而前面部分為空白,也需要用“:”格開,相應部分內容為空白。例如:
__asm__ __volatile__("cli": : :"memory")
1、彙編語句模板
彙編語句模板由彙編語句序列組成,語句之間使用“;”、“/n”或“/n/t”分開。指令中的運算元可以使用預留位置引用C語言變數,運算元預留位置最多10個,名稱如下:%0,%1,…,%9。指令中使用預留位置表示的運算元,總被視為long型(4個位元組),但對其施加的操作根據指令可以是字或者位元組,當把運算元當作字或者位元組使用時,預設為低字或者低位元組。對位元組操作可以顯式的指明是低位元組還是次位元組。方法是在%和序號之間插入一個字母,“b”代表低位元組,“h”代表高位元組,例如:%h1。
2、輸出部分
輸出部分描述輸出運算元,不同的運算元描述符之間用逗號格開,每個運算元描述符由限定字串和C 語言變數組成。每個輸出運算元的限定字串必須包含“=”表示他是一個輸出運算元。
例:
__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )
描述符字串表示對該變數的限制條件,這樣GCC 就可以根據這些條件決定如何分配寄存器,如何產生必要的代碼處理指示運算元與C運算式或C變數之間的聯絡。
3、輸入部分
輸入部分描述輸入運算元,不同的運算元描述符之間使用逗號格開,每個運算元描述符由限定字串和C語言運算式或者C語言變數組成。
例1 :
__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
例二(bitops.h):
Static __inline__ void __set_bit(int nr, volatile void * addr) { __asm__( "btsl %1,%0" :"=m" (ADDR) :"Ir" (nr)); } |
後例功能是將(*addr)的第nr位設為1。第一個預留位置%0與C 語言變數ADDR對應,第二個預留位置%1與C語言變數nr對應。因此上面的彙編語句代碼與下面的虛擬碼等價:btsl nr, ADDR,該指令的兩個運算元不能全是記憶體變數,因此將nr的限定字串指定為“Ir”,將nr 與立即數或者寄存器相關聯,這樣兩個運算元中只有ADDR為記憶體變數。
4、限制字元
4.1、限制字元列表
限制字元有很多種,有些是與特定體繫結構相關,此處僅列出常用的限定字元和i386中可能用到的一些常用的限定符。它們的作用是指示編譯器如何處理其後的C語言變數與指令運算元之間的關係。
分類 限定符 描述
通用寄存器 “a” 將輸入變數放入eax
這裡有一個問題:假設eax已經被使用,那怎麼辦?
其實很簡單:因為GCC 知道eax 已經被使用,它在這段彙編代碼
的起始處插入一條語句pushl %eax,將eax 內容儲存到堆棧,然
後在這段代碼結束處再增加一條語句popl %eax,恢複eax的內容
“b” 將輸入變數放入ebx
“c” 將輸入變數放入ecx
“d” 將輸入變數放入edx
“s” 將輸入變數放入esi
“d” 將輸入變數放入edi
“q” 將輸入變數放入eax,ebx,ecx,edx中的一個
“r” 將輸入變數放入通用寄存器,也就是eax,ebx,ecx,
edx,esi,edi中的一個
“A” 把eax和edx合成一個64 位元的寄存器(use long longs)
記憶體 “m” 記憶體變數
“o” 運算元為記憶體變數,但是其定址方式是位移量類型,
也即是基址定址,或者是基址加變址定址
“V” 運算元為記憶體變數,但定址方式不是位移量類型
“ ” 運算元為記憶體變數,但定址方式為自動增量
“p” 運算元是一個合法的記憶體位址(指標)
寄存器或記憶體 “g” 將輸入變數放入eax,ebx,ecx,edx中的一個
或者作為記憶體變數
“X” 運算元可以是任何類型
立即數
“I” 0-31之間的立即數(用於32位移位指令)
“J” 0-63之間的立即數(用於64位移位指令)
“N” 0-255之間的立即數(用於out指令)
“i” 立即數
“n” 立即數,有些系統不支援除字以外的立即數,
這些系統應該使用“n”而不是“i”
匹配 “ 0 ”, 表示用它限制的運算元與某個指定的運算元匹配,
“1” ... 也即該運算元就是指定的那個運算元,例如“0”
“9” 去描述“%1”運算元,那麼“%1”引用的其實就
是“%0”運算元,注意作為限定符字母的0-9 與
指令中的“%0”-“%9”的區別,前者描述運算元,
後者代表運算元。
& 該輸出運算元不能使用過和輸入運算元相同的寄存器
運算元類型 “=” 運算元在指令中是唯寫的(輸出運算元)
“+” 運算元在指令中是讀寫類型的(輸入輸出運算元)
浮點數 “f” 浮點寄存器
“t” 第一個浮點寄存器
“u” 第二個浮點寄存器
“G” 標準的80387浮點常數
% 該運算元可以和下一個運算元交換位置
例如addl的兩個運算元可以交換順序
(當然兩個運算元都不能是立即數)
# 部分注釋,從該字元到其後的逗號之間所有字母被忽略
* 表示如果選用寄存器,則其後的字母被忽略
5、破壞描述部分
破壞描述符用於通知編譯器我們使用了哪些寄存器或記憶體,由逗號格開的字串組成,每個字串描述一種情況,一般是寄存器名;除寄存器外還有“memory”。例如:“%eax”,“%ebx”,“memory”等。
本文引自:
http://blog.21ic.com/tb.asp?id=28654&TBcode=2008022500461ix3ConBAUcX