第5部分-Linux x86 64位彙編 AT&T彙編
關於調試我們放到後面,因爲這篇開始還是進入本系列的正題了。
學習了前面的INTEL 彙編,開始使用AT&T彙編了。
不是所有彙編器使用的標準都一樣的,不通匯編器使用不同的彙編語法。
關於AT&T彙編,也就是基於gas彙編器的。可以參考書籍《Programming Ground Up》。
AT&T彙編程序的結構跟其它彙編語言類似,由directives, labels, instructions組成,助記符最多可以跟隨三個操作數。與Intel彙編語言相比,最大的區別在於操作數的順序。
這裏要提醒的是:這些都會在實例中來學習鞏固,死記還是比較難的,在實踐中不斷理解即可。
比如Intel彙編語法的傳送指令通常是:
mnemonic destination,source
在AT&T彙編中,通常是:
mnemonic source,destination
- 也即源操作數在左邊,目的操作數在右邊。
- 所有寄存器前面都要加前綴%.
- 所有立即數必須冠以前綴$.
- 符號常數直接引用,引用符號地址在符號前加符號$
- 通過對操作指令添加b/w/l/q等後綴來指示操作數“尺寸”. Intel 語法通過在內存操作數(而不是操作碼本身)前面加 byte ptr、word ptr 和 dword ptr 來指定大小。
jmp, call, ret這些指令用於控制程序從某條指令跳到另外的指令處。它又分爲近跳轉(在同一段內)和遠跳轉(不同段內)。跳轉地址可以用相對偏移(label),寄存器,內存操作數.
- 中間形式長跳轉和調用是 lcall/ljmp $section, $offset;Intel 語法是 call/jmp far section:offset。在 AT&T 語法中,遠返回指令是 lret $stack-adjust,而 Intel 使用 ret far stack-adjust。
- GAS中的內存操作數的尋址方式是section:disp(base, index, scale)。%segment:ADDRESS (, index, multiplier)或%segment:(offset, index, multiplier)或%segment:ADDRESS(base, index, multiplier)。ADDRESS or offset + base + index * multiplier。NASM 使用的語法比較簡單。上面的公式在 NASM 中表示爲:Segment:[ADDRESS or offset + index * multiplier]
- GAS 中,重複結構以.rept指令開頭,用一個.endr指令結束這個指令。.rept後面是一個數字,指定.rept/.endr結構中表達式重複執行的次數。相當於編寫這個指令count次,每次重複佔據單獨的一行。NASM 中,在預處理器級使用相似的結構。它以 %rep 指令開頭,以%endrep結尾。%rep指令後面是一個表達式。NASM 中還有另一種結構,times指令。與%rep相似,它也在彙編級起作用。
在AT&T語法中,符號擴展和零擴展指令的格式爲,基本部分"movs"和"movz"(對應Intel語法的movsx和movzx),後面跟源操作數長度和目的操作數長度。movsbl意味着movs (from)byte (to)long;movsbw意味着movs (from)byte (to)word;movswl意味着movs (from)word (to)long。對於movz指令也一樣。
以點號開頭的任何內容都不會直接轉換爲機器指令。例如.section .data表示數據段,.section .text表示代碼段,.globl意味着彙編程序在彙編後不應丟棄此符號,因爲鏈接器將需要它。 _start是一個特殊的符號,總是需要用.globl標記,因爲它標記了程序開始的位置。
函數定義.type funcname,@function,定義函數。
- GAS 中的彙編器指令以 “.” 開頭,但是在 NASM 中不是。
- GAS 支持 C 風格(/* */)、C++ 風格(//)和 shell 風格(#)的註釋。NASM 支持以 “;” 字符開頭的單行註釋。
- NASM 分別使用 dd、dw 和 db 指令聲明 32 位、16 位和 8 位數字,而 GAS 分別使用 .long、.int 和 .byte。GAS 還有其他指令,如 .ascii、.asciz 和 .string。在 GAS 中,像聲明其他標籤一樣聲明變量(使用冒號),在 NASM 中,只需在內存分配指令(dd、dw 等等)前面輸入變量名,後面加上變量的值。
- 內存直接尋址模式。NASM 使用方括號間接引用一個內存位置指向的地址值:[var1]。GAS 使用圓括號間接引用同樣的值:(var1)
- 字符串的地址 ,在 NASM 中,內存變量代表內存位置本身,所以 push str 這樣的調用實際上是將地址壓入堆棧的頂部。在 GAS中,變量 str 必須加上前綴 $,纔會被當作地址。如果不加前綴 $,那麼會將內存變量代表的實際字節,而不是地址。
- 關於註釋,Nasm是;,而在AT&T中是# 單行註釋;// 單行註釋;/* */ 多行註釋
當然這些都會在實例中來學習鞏固,死記還是比較難的,實踐中不斷理解即可。
數據聲明
AT&T中數據聲明如下:
命令 數據類型
.ascii 文本字符串
.asciz 以空字符串結尾的文本字符串
.byte 字節值
.double 雙精度浮點數
.float 單精度浮點數
.int 32位整數
.long 32位整數(同32)
.octa 16字節整數
.quad 8字節整數
.short 16位整數
.single 單精度浮點數(和.float同)
此外匯編器使用兩個命令聲明緩衝:
命令 描述
.comm 聲明未初始化的數據的通用內存區域
.lcomm 聲明未初始化的數據的本地通用內存區域
movx,其中x可以是下面字符:
x 描述
q 用於64位的長字值
l 用於32位的長字值
w 用於16位的字值
b 用於8位的字節值
後面我們會給出幾個常用的AT&T語法彙編例子。