.text
.globl _start
_start:
popl %ecx # argc
vnext:
popl %ecx # ar**
test %ecx, %ecx # 空指標表明結束
jz exit
movl %ecx, %ebx
xorl %edx, %edx
strlen:
movb (%ebx), %al
inc %edx
inc %ebx
test %al, %al
jnz strlen
movb $10, -1(%ebx)
movl $4, %eax # 系統呼叫號(sys_write)
movl $1, %ebx # 檔案描述符(stdout)
int $0x80
jmp vnext
exit: movl $1,%eax # 系統呼叫號(sys_exit)
xorl %ebx, %ebx # 退出**
int $0x80
ret六、gcc 內聯彙編
用彙編編寫的程式雖然執行速度快,但開發速度非常慢,效率也很低。如果只是想對關鍵**段進行優化,或許更好的辦法是將彙編指令嵌入到 c 語言程式中,從而充分利用高階語言和組合語言各自的特點。但一般來講,在 c **中嵌入彙編語句要比"純粹"的組合語言**複雜得多,因為需要解決如何分配暫存器,以及如何與c**中的變數相結合等問題。
gcc 提供了很好的內聯彙編支援,最基本的格式是:
asm(「asm statements」);
例如:asm(「nop」);
如果需要同時執行多條彙編語句,則應該用"\n\t"將各個語句分隔開,例如:
asm( 「pushl %%eax \n\t」
「movl $0, %%eax \n\t」
「popl %eax」);
通常嵌入到 c **中的彙編語句很難做到與其它部分沒有任何關係,因此更多時候需要用到完整的內聯彙編格式:
asm(「asm statements」 : outputs : inputs : registers-modified);
插入到 c **中的彙編語句是以":「分隔的四個部分,其中第一部分就是彙編**本身,通常稱為指令部,其格式和在組合語言中使用的格式基本相同。指令部分是必須的,而其它部分則可以根據實際情況而省略。
在將彙編語句嵌入到c**中時,運算元如何與c**中的變數相結合是個很大的問題。gcc採用如下方法來解決這個問題:程式設計師提供具體的指令,而對暫存器的使用則只需給出"樣板"和約束條件就可以了,具體如何將暫存器與變數結合起來完全由gcc和gas來負責。
在gcc 內聯彙編語句的指令部中,加上字首』』%』『的數字(如%0,%1)表示的就是需要使用暫存器的"樣板"運算元。指令部中使用了幾個樣板運算元,就表明有幾個變數需要與暫存器相結合,這樣gcc和gas在編譯和彙編時會根據後面給定的約束條件進行恰當的處理。由於樣板運算元也使用』』 %』『作為字首,因此在涉及到具體的暫存器時,暫存器名前面應該加上兩個』』%』』,以免產生混淆。
緊跟在指令部後面的是輸出部,是規定輸出變數如何與樣板運算元進行結合的條件,每個條件稱為乙個"約束」,必要時可以包含多個約束,相互之間用逗號分隔開就可以了。每個輸出約束都以』』=』『號開始,然後緊跟乙個對運算元型別進行說明的字後,最後是如何與變數相結合的約束。凡是與輸出部中說明的運算元相結合的暫存器或運算元本身,在執行完嵌入的彙編**後均不保留執行之前的內容,這是gcc在排程暫存器時所使用的依據。
輸出部後面是輸入部,輸入約束的格式和輸出約束相似,但不帶』』=』'號。如果乙個輸入約束要求使用暫存器,則gcc在預處理時就會為之分配乙個暫存器,並插入必要的指令將運算元裝入該暫存器。與輸入部中說明的運算元結合的暫存器或運算元本身,在執行完嵌入的彙編**後也不保留執行之前的內容。
有時在進行某些操作時,除了要用到進行資料輸入和輸出的暫存器外,還要使用多個暫存器來儲存中間計算結果,這樣就難免會破壞原有暫存器的內容。在gcc內聯彙編格式中的最後乙個部分中,可以對將產生***的暫存器進行說明,以便gcc能夠採用相應的措施。
下面是乙個內聯彙編的簡單例子:
例4.內聯彙編
int main()
上面的程式完成將變數a的值賦予變數b,有幾點需要說明:
變數b是輸出運算元,通過%0來引用,而變數a是輸入運算元,通過%1來引用。
輸入運算元和輸出運算元都使用r進行約束,表示將變數a和變數b儲存在暫存器中。輸入約束和輸出約束的不同點在於輸出約束多乙個約束修飾符』』=』』。
在內聯彙編語句中使用暫存器eax時,暫存器名前應該加兩個』』%』』,即%%eax。內聯彙編中使用%0、%1等來標識變數,任何只帶乙個』』%』『的識別符號都看成是運算元,而不是暫存器。
內聯彙編語句的最後乙個部分告訴gcc它將改變暫存器eax中的值,gcc在處理時不應使用該暫存器來儲存任何其它的值。
由於變數b被指定成輸出運算元,當內聯彙編語句執行完畢後,它所儲存的值將被更新。
在內聯彙編中用到的運算元從輸出部的第乙個約束開始編號,序號從0開始,每個約束記數一次,指令部要引用這些運算元時,只需在序號前加上』』%』'作為字首就可以了。需要注意的是,內聯彙編語句的指令部在引用乙個運算元時總是將其作為32位的長字使用,但實際情況可能需要的是字或位元組,因此應該在約束中指明正確的限定符:
限定符意義
「m」、「v」、「o」
記憶體單元
「r」任何暫存器
「q」暫存器eax、ebx、ecx、edx之一
「i」、「h」
直接運算元
「e"和"f」
浮點數「g」
任意「a」、「b」、「c」、「d」
分別表示暫存器eax、ebx、ecx和edx
「s"和"d」
暫存器esi、edi
「i」常數(0至31)
AT T彙編語法格式
1.暫存器的引用要在暫存器前加 如mov eax,ebx 2.運算元排列是左源右目的,如上例表示把值從eax暫存器mov到ebx暫存器 3.常數 立即數前面要加 如mov 4,ebx 4.對於變數加 表示取位址。如mov value,ebx表示傳值給ebx,而mov value,ebx表示傳位址給e...
Linux下AT T彙編語法格式簡介
一 at t 格式linux 彙編語法格式 在 at t 彙編格式中,暫存器名要加上 作為字首 而在 intel 彙編格式中,暫存器名不需要加字首。例如 at t格式 intel格式 pushl eax push eax 在 at t 彙編格式中,用 字首表示乙個立即運算元 而在 intel 彙編格...
Linux下AT T彙編語法格式簡介
絕大多數linux程式設計師以前只接觸過dos windows下的組合語言,這些彙編 都是intel風格的。但在unix和linux系統中,更多採用的還是at t格式,兩者在語法格式上有著很大的不同 在at t彙編格式中,暫存器名要加上 作為字首 而在intel彙編格式中,暫存器名不需要加字首。例如...