轉載 http://www.360doc.com/content/10/0926/12/1317564_56492037.shtmlhtml
彙編基本語法簡介
在 AT&T 彙編格式中,寄存器名要加上 '%' 做爲前綴;而在 Intel 彙編格式中,寄存器名不須要加前綴。例如:
AT&T 格式
Intel 格式
pushl %eax
push eax
學習
在 AT&T 彙編格式中,用 '$' 前綴表示一個當即操做數;而在 Intel 彙編格式中,當即數的表示不用帶任何前綴。例如:
AT&T 格式
Intel 格式
pushl $1
push 1
指針
AT&T 和 Intel 格式中的源操做數和目標操做數的位置正好相反。在 Intel 彙編格式中,目標操做數在源操做數的左邊;而在 AT&T 彙編格式中,目標操做數在源操做數的右邊。例如:
AT&T 格式
Intel 格式
addl $1, %eax
add eax, 1
orm
在 AT&T 彙編格式中,操做數的字長由操做符的最後一個字母決定,後綴'b'、'w'、'l'分別表示操做數爲字節(byte,8 比特)、字(word,16 比特)和長字(long,32比特);而在 Intel 彙編格式中,操做數的字長是用 "byte ptr" 和 "word ptr" 等前綴來表示的。例如:
AT&T 格式
Intel 格式
movb val, %al
mov al, byte ptr val
htm
在 AT&T 彙編格式中,絕對轉移和調用指令(jump/call)的操做數前要加上'*'做爲前綴,而在 Intel 格式中則不須要。
遠程轉移指令和遠程子調用指令的操做碼,在 AT&T 彙編格式中爲 "ljump" 和 "lcall",而在 Intel 彙編格式中則爲 "jmp far" 和 "call far",即:
AT&T 格式 Intel 格式
ljump $section, $offset
jmp far section:offset
lcall $section, $offset
call far section:offset
內存
與之相應的遠程返回指令則爲:
AT&T 格式
Intel 格式
lret $stack_adjust
ret far stack_adjust
文檔
在 AT&T 彙編格式中,內存操做數的尋址方式是
AT&T 格式
Intel 格式
section:disp(base, index, scale)
section:[base + index*scale + disp]
字符串
因爲 Linux 工做在保護模式下,用的是 32 位線性地址,因此在計算地址時不用考慮段基址和偏移量,而是採用以下的地址計算方法:disp + base + index * scale
下面是一些內存操做數的例子:
AT&T 格式
Intel 格式
movl -4(%ebp), %eax
mov eax, [ebp - 4]
movl array(, %eax, 4), %eax
mov eax, [eax*4 + array]
movw array(%ebx, %eax, 4), %cx
mov cx, [ebx + 4*eax + array]
movb $4, %fs:(%eax)
mov fs:eax, 4
編譯器
內嵌彙編格式簡介
內嵌彙編語法以下:
__asm__(彙編語句模板: 輸出部分: 輸入部分: 破壞描述部分)
it
其中,asm 和 __asm__是徹底同樣的。共四個部分:彙編語句模板,輸出部分,輸入部分,破壞描述部分,各部分使用「:」格開,彙編語句模板必不可少,其餘三部分可選,若是使用了後面的部分,而前面部分爲空,也須要用「:」格開,相應部份內容爲空。例如:
__asm__ __volatile__("cli": : :"memory")
一、彙編語句模板
彙編語句模板由彙編語句序列組成,語句之間使用 「;」、「\\n」或「\\n\\t」分開。指令中的操做數可使用佔位符引用C語言變量,操做數佔位符最多10個,名稱以下:%0,%1,…,%9。指令中使用佔位符表示的操做數,總被視爲long型(4個字節),但對其施加的操做根據指令能夠是字或者字節,當把操做數看成字或者字節使用時,默認爲低字或者低字節。對字節操做能夠顯式的指明是低字節仍是次字節。方法是在%和序號之間插入一個字母,「b」表明低字節,「h」表明高字節,例如:%h1。
二、輸出部分
輸出部分描述輸出操做數,不一樣的操做數描述符之間用逗號格開,每一個操做數描述符由限定字符串和C 語言變量組成。每一個輸出操做數的限定字符串必須包含「=」表示他是一個輸出操做數。
例:
__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )
描述符字符串表示對該變量的限制條件,這樣GCC 就能夠根據這些條件決定如何分配寄存器,如何產生必要的代碼處理指令操做數與C表達式或C變量之間的聯繫。
三、輸入部分
輸入部分描述輸入操做數,不一樣的操做數描述符之間使用逗號格開,每一個操做數描述符由限定字符串和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.一、限制字符列表
限制字符有不少種,有些是與特定體系結構相關,此處僅列出經常使用的限定字符和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的兩個操做數能夠交換順序(固然兩個操做數都不能是當即數)
#
部分註釋,從該字符到其後的逗號之間全部字母被忽略
*
表示若是選用寄存器,則其後的字母被忽略
五、破壞描述部分 破壞描述符用於通知編譯器咱們使用了哪些寄存器或內存,由逗號格開的字符串組成,每一個字符串描述一種狀況,通常是寄存器名;除寄存器外還有「memory」。例如:「%eax」,「%ebx」,「memory」等。感謝這篇文章是兩篇文章的綜合體,我只是把這兩篇文章綜合起來了,進行了一下簡單的排版,閱讀起來方便一點,舒服一點。兩位做者分別是肖文鵬和臨江仙,向他們表示感謝。後續我會根據相關資料,繼續改進該文檔,使之更全面。Mail:normalnotebook@126.com,互相學習。