歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

Linux GCC內嵌匯編基礎知識



在Linux代碼中很多地方都使用了這種形式的匯編語言,嵌入匯編程序的格式如下:

__asm__ __volatile__ (

asm statements

: outputs

: inputs

: registers-modified

);

asm statements是一組AT&T格式的匯編語言語句,每個語句一行,由\n分隔各行。所有的語句都被包裹在一對雙引號內。其中使用的寄存器前面要加兩個%%做前綴(%n表示參數,n:數字);轉移指令多是局部轉移,因此多使用數字標號。

inputs指明程序的輸入參數,每個輸入參數都括在一對圓括號內,各參數用逗號分開。每個參數前加一個用雙引號括起來的標志,告訴編譯器把該參數裝入到何處。

可用的標志有:

“g”:讓編譯器決定如何裝入它;

“a”:裝入到ax/eax;

“b”:裝入到bx/ebx;

“c”:裝入到cx/ecx;

“d”:裝入到dx/edx;

“D”:裝入到di/edi;

“S”:裝入到si/esi;

“q”:a、b、c、d寄存器等;

“r”:任一通用寄存器;

“i”:整立即數;

“I”:0-31 之間的立即數(用於32位移位指令);

“J”:0-63 之間的立即數(用於64 位移位指令);

“N”:0-255 ,之間的立即數(用於out 指令);

“n”:立即數,有些系統不支持除字以外的立即數,這些系統應該使用“n”而不是“i”;

“p”:有效內存地址;

“m”:內存變量;

“o”:操作數為內存變量,但是其尋址方式是偏移量類型,也即是基址尋址,或者是基址加變址尋址;

“V”:操作數為內存變量,但尋址方式不是偏移量類型;

“,”:操作數為內存變量,但尋址方式為自動增量;

“X”:操作數可以是任何類型;

“f”:浮點數;

“t”:第一個浮點寄存器;

“u”:第二個浮點寄存器;

“G”:標准的80387;

% :浮點常數,該操作數可以和下一個操作數交換位置;

“=”:輸出;

“+”:既是輸入又是輸出;

“&”:改變其值之前寫,分配一個獨立的寄存器,使得函數返回值和該變量不因為重復使用同一個寄存器,出現數據覆蓋;

“%”:與下一個操作數之間可互換;

“#”:忽略其後的字符,直到逗號;

“*”:當優先選擇寄存器時,忽略下面的字符;

“0”~“9”:指定一個操作數,它既做輸入又做輸出。通常用“g”;

outputs指明程序的輸出位置,通常是變量。每個輸出變量都括在一對圓括號內,各個輸出變量間用逗號隔開。每個輸出變量前加一個標志,告訴編譯器從何處輸出。

可用的標志與輸入參數用的標志相同,只是前面加“=”。如“=g”。輸出操作數必須是左值,而且必須是只寫的。如果一個操作數即做輸出又做輸入,那麼必須將它們分開:一個只寫操作數,一個輸入操作數。輸入操作數前加一個數字限制(0~9),指出輸出操作數的序號,告訴編譯器它們必須在同一個物理位置。兩個操作數可以是同一個表達式,也可以是不同的表達式。

registers-modified告訴編譯器程序中將要修改的寄存器。每個寄存器都用雙引號括起來,並用逗號隔開。如“ax”。如果匯編程序中引用了某個特定的硬件寄存器,就應該在此處列出這些寄存器,以告訴編譯器這些寄存器的值被改變了。如果匯編程序中用某種不可預測的方式修改了內存,應該在此處加上“memory”。這樣以來,在整個匯編程序中,編譯器就不會把它的值緩存在寄存器中了。

如:

“cc”:你使用的指令會改變CPU的條件寄存器cc;

“memory”:你使用的指令會修改內存;

__volatile__是可選的,它防止編譯器修改該段匯編語句(重排序、重組、刪除等)。

輸入參數和輸出變量按順序編號,先輸出後輸入,編號從0開始。程序中用編號代表輸入參數和輸出變量(加%做前綴)。

輸入、輸出、寄存器部分都可有可無。如有,順序不能變;如無,應保留“:”,除非不引起二意性。

看一個在C語言中使用at&t的嵌入匯編程序的例子,c語言中的3個int變量,一般會是三個內存地址。每個操作數的長度則要根據操作系統和編譯器來決定,一般32位操作系統為32位,則每個操作數占用4個字節:

int i=0, j=1, k=0;

__asm__ __volatile__("

pushl %%eax\n //asm statement

movl %1, %%eax\n //asm statement

addl %2, %%eax\n //asm statement

movl %%eax, %0\n //...

popl %%eax" //...

: "=g" (k) //outputs

: "g" (i), "g" (j) //inputs

: "ax", "memory" //registers modified

);

按照參數編號原則輸出參數參數k為%0,輸入參數i和j依次為%1和%2。值得注意的是輸出和輸入標志都使用了"g",所以我們不必關心這些參數究竟是使用了寄存器還是內存操作數,編譯器自己會決定。

Copyright © Linux教程網 All Rights Reserved