本文將對AT&T彙編進行簡單的介紹,如果要瞭解詳細情況,請查閱GNU as的參考手冊(http://sourceware.org/binutils/docs-2.16/as/index.html)。
剛開始接觸AT&T彙編的時候,很多人都有點犯糊塗。但是如果你有過編寫組譯工具的經驗,只要記住幾個要點,對它應該還是比較容易讓手。在以下的介紹中,我將使用NASM文法與之對照。
GNU as是GNU編譯器的一個支援(後端)程式。也許as並不適合編寫大型的組譯工具,但無論如何,它是當代unix族作業系統的一個重要組件,尤其是編寫核心時。因為它文法有點晦澀,所以,經常被人詬病,認為它不“友好”,過分地強調作為GCC的一個後端程式。如果你以前只有Intel彙編文法背景,你學習起來會稍微感到沉悶。但不管怎樣,現在很多作業系統的底層代碼就是用as彙編實現的(比如linux),如果你有興趣研究linux核心代碼的話,最好還是具備一點AT&T彙編的知識。
基本格式:
AT&T組譯工具的結構跟其它組合語言類似,由directives, labels, instructions組成,助記符最多可以跟隨三個運算元。與其它組合語言相比,最大的區別在於運算元的順序,比如Intel彙編文法的傳送指令通常是:
mnemonic destination,source
然而,在AT&T彙編中,通常是:
mnemonic source,destination
也即源運算元在左邊,目的運算元在右邊。
接下來介紹AT&T彙編在x86架構下運算元的類型:
1.寄存器
所有寄存器前面都要加首碼%。比如%al,%bx,%cr0。
move %ax,%bx
上面指令將移動寄存器ax的值到寄存器bx。
2.立即數
所有立即數必須冠以首碼$,比如:
move $100,%bx
mov $A,%al
上面第一條指令將把100放入寄存器ax中;第二條指令把A的ascii碼放入al寄存器。
3.記憶體位址
在AT&T彙編文法中,記憶體通過下面方式表達:
segment-override:sigend-offset(base,index,scale)
比如:(注意offset和scale不應該加首碼$)
%es:100(%eax,%ebx,2)
其中某些部分在實際應用中可省略。
與NASM文法的比較:
GAS memory operand NASM memory operand
------------------ -------------------
100 [100]
%es:100 [es:100]
(%eax) [eax]
(%eax,%ebx) [eax+ebx]
(%ecx,%ebx,2) [ecx+ebx*2]
(,%ebx,2) [ebx*2]
-10(%eax) [eax-10]
%ds:-10(%ebp) [ds:ebp-10]
樣本:
move %ax,100
mov %eax,-100(%eax)
上面第一條指令移動ax寄存器內容到記憶體位址100處;第二條指令一定eax寄存器內容到eax值-100的記憶體位址。
運算元大小:
經常,尤其是把立即數寫入記憶體時,需要指定運算元的“尺寸”。比如:
mov $10,100
這個資訊是不完整的,是把整數10寫到地址開始為100的一個位元組呢還是一個字?在NASM中,通過轉換關鍵字byte/word/dword 來指定該“尺寸”。在AT&T彙編中,通過對操作指令添加b/w/l等尾碼來指示運算元“尺寸”。比如:
movb $10,%es:(%eax) #寫入一個位元組
movl $10,%es:(%eax) #寫入一個雙字
其餘一些例子:
movl $100, %ebx
pushl %eax
popw %ax
控制轉移指令:
jmp, call, ret這些指令用於控製程序從某條指令跳到另外的指令處。它又分為近跳轉(在同一段內)和遠跳轉(不同段內)。跳轉地址可以用相對位移(label),寄存器,記憶體運算元,Segment-offset指標表示:
1.相對位移(label)
label1:
...
jmp label1
2.寄存器或記憶體運算元
必須添加首碼*。如果是遠跳轉,還要對跳轉指令加上l首碼,例子:
GAS syntax NASM syntax
========== ===========
jmp *100 jmp near [100]
call *100 call near [100]
jmp *%eax jmp near eax
jmp *%ecx call near ecx
jmp *(%eax) jmp near [eax]
call *(%ebx) call near [ebx]
ljmp *100 jmp far [100]
lcall *100 call far [100]
ljmp *(%eax) jmp far [eax]
lcall *(%ebx) call far [ebx]
ret retn
lret retf
lret $0x100 retf 0x100
3.Segment-offset
形如:
jmp $segment, $offset
樣本:
jmp $0x10, $0x100000
這些就是AT&T彙編最基本的東西,應該很容易。把上面這些牢記在心中,看懂GAS的彙編代碼不是太難。如果想閱覽linux核心底層源碼的話,再瞭解一下GCC內嵌彙編格式,就可以開始了~~