Linux環境組合語言程式設計初步——AT&T語法

NO IMAGE
Linux環境組合語言程式設計初步——AT&T語法
 
 
 
目前正在學習Linux彙編,寫寫文章共享一下心得

組合語言作為一種高效的,而且緊密結合硬體平臺的程式語言,在作業系統,嵌入式開發等領域都有著十分重要的作用。正因為彙編依賴於硬體結構(CPU指令碼),因此不同體系結構上的組合語言也大相徑庭。目前國內大學的組合語言課程大多以Intel平臺的語法格式來講述,而市面上講述其他體系結構組合語言的書籍也是寥寥無幾,這就給系統程式設計師研究其他平臺彙編程式碼帶來了很大的困難。本文簡單介紹了Linux下的AT&T語法,以及在Linux下彙編的基本方法。

AT&T語法起源於AT&T貝爾實驗室,是在當時用於實現Unix系統的處理器操作碼語法之上而形成的,AT&T語法和Intel語法主要區別如下:
AT&T使用$表示立即數,Intel不用,因此表示十進位制2時,AT&T為$2,而Intel就是2
AT&T在暫存器前加%,比如eax暫存器表示為%eax
AT&T 處理運算元的順序和Intel相反,比如,movl %eax, %ebx是將eax中的值傳遞給ebx,而Intel是這樣的mov ebx, eax
AT&T在助記符的後面加上一個單獨字元表示操作中資料的長度,比如movl $foo, %eax等同於Intel的mov eax, word ptr foo
長跳轉和呼叫的格式不同,AT&T為ljmp $section, $offset,而Intel為jmp section:offset
主要的區別就是這些,其他的細節還有很多,下面給出一個具體的例子來說明

CODE:
#cpuid.s Sample program

.section .data

output:
.ascii “The processor Vendor ID is ‘xxxxxxxxxxxx’/n”

.section .text
.globl _start

_start:

movl $0, %eax

cpuid

movl $output, %edi

movl %ebx, 28(%edi)

movl %edx, 32(%edi)

movl %ecx, 36(%edi)

movl $4, %eax

movl $1, %ebx

movl $output, %ecx

movl $42, %edx

int $0x80

movl $1, %eax

movl $0, %ebx

int $0x80

這個程式的作用是查詢CPU的廠商ID,其中:

,ascii定義字串(和Intel格式完全不同).section是宣告段的語句,.data和.text是段名,分別為資料段和程式碼段,_start是gas(GNU彙編器)的預設入口標籤,表示程式從這裡開始執行。.globl將_start宣告成了外部程式訪問的標籤。cpuid為指令請求CPU的指定資訊,該指令用eax作為輸入,ebx,edx,ecx作為輸出,這裡將0作為cpuid的輸入指令,請求返回CPU的廠商ID字串。返回的結果,一個12位元組的字串,分別儲存在三個暫存器中,其中ebx存放低4位,edx中間4位,ecx高4位(注意順序!)。接下來定義一個指標edi,edi指向output的開始地址,然後接著的3條語句將output裡的x替換為廠商資訊。28(%edi)中的28表示偏移量,即整個地址為%edi裡的地址加上28個位元組,這個地址正好是output裡第一個x的地址。再接下來就是列印結果了,這裡用到了Linux的一個系統呼叫(int 0x80),該系統呼叫的引數分別為:eax 系統呼叫號,ebx 要寫入的檔案描述符,ecx 字串首地址,edx 字串長度,程式裡這些個引數的值分別為4,1(標準輸出),output的地址和42。最後再次呼叫1號系統呼叫-退出函式,返回shell,這次ebx中的值是返回給shell的退出程式碼,0表示無異常

然後彙編連線執行程式:

CODE:
$as -o cpuid.o cpuid.s

$ld -o cpuid cpuid.o

$./cpuid

The processor Vendor ID is ‘GenuineIntel’

$

本人的電腦是Pentium M的CPU所以返回的結果是GenuineIntel。

幾點說明:

1)Linux的標準彙編環境為as,ld,gdb,gprof,objdump等GNU開發除錯工具,除了gdb外,其他全部隨binutils包釋出。其中as使用的是AT&T語法。在Linux下也可以使用Nasm來進行Intel格式的彙編程式編寫,具體Nasm的使用方法見本人blog的《Nasm使用手冊》

2)Linux下彙編的系統呼叫為int 0x80,和DOS下的int 21h大同小異,只不過傳遞引數不同

3)段宣告語句.section不需要像Intel格式那樣在段結尾的時候加上段結束標誌(SEGMENT/ENDS),下一個段的開始自動標誌著上個段的結束

4)簡單程式的入口標籤不是必須要定義的,ld會自己判斷入口,但是會給出警告

5)本文部分內容來源於Richard Blum的《Professional Assembly Language》一書