Linux的gcc可以看汇编,linux gcc 内嵌汇编
通常嵌入到 C 代碼中的匯編語句很難做到與其它部分沒有任何關系,因此更多時候需要用到完整的內聯匯編格式,即匯編模板:
__asm__ ?__volatile__ ("asm statements" : outputs : inputs : registers-modified);
__asm__
表示后面的代碼為內嵌匯編,
asm
是
__asm__
的別名。
__volatile__
表示編譯器不要優化代碼,后面的指令保留原樣,
volatile
是它的別名。括號里面是匯編指令。
插入到 C 代碼中的匯編語句是以":"分隔的四個部分,其中第一部分就是匯編代碼本身,通常稱為指令部,其格式和在匯編語言中使用的格式基本相同。指令部分是必須的,而其它部分則可以根據實際情況而省略。如果使用了后面的部分,而前面部分為空,也需要用“:”格開,相應部分內容為空。
下面介紹模板中的四個部分:
1、匯編語句模板匯編語句模板由匯編語句序列組成,語句之間使用“;”、“\n”或“\n\t”分開。指令中的操作數可以使用占位符引用C語言變量,操作數占位符最多10個,名稱如下:%0,%1,…,%9。指令中使用占位符表示的操作數,總被視為long型(4個字節),但對其施加的操作根據指令可以是字或者字節,當把操作數當作字或者字節使用時,默認為低字或者低字節。對字節操作可以顯式的指明是低字節還是次字節。方法是在%和序號之間插入一個字母,b代表低字節,h 代表高字節,例如:%h1。2、輸出部分輸出部分描述輸出操作數,不同的操作數描述符之間用逗號格開,每個操作數描述符由限定字符串和C 語言變量組成。每個輸出操作數的限定字符串必須包含“=”表示他是一個輸出操作數。例如:__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )描述符字符串表示對該變量的限制條件,這樣GCC就可以根據這些條件決定如何分配寄存器,如何產生必要的代碼處理指令操作數與C表達式或C變量之間的聯系。3、輸入部分輸入部分描述輸入操作數,不同的操作數描述符之間使用逗號格開,每個操作數描述符由限定字符串和C 語言表達式或者C語言變量組成。示例如下:例1:
__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
例
2
:
Static __inline__ void __set_bit(int nr, volatile void * addr)
{
__asm__(
"btsl %1,%0"
:"=m" (ADDR)
:"Ir" (nr));
}
后例功能是將
(*addr)
的第
nr
位設為
1
。第一個占位符
%0
與
C
語言變量
ADDR
對應,第二個占位符
%1
與
C
語言變量
nr
對應。因此上面的匯編語句代碼與下面的偽代碼等價:
btsl nr, ADDR
,該指令的兩個操作數不能全是內存變量,因此將
nr
的限定字符串指定為
“Ir”
,將
nr
與立即數或者寄存器相關聯,這樣兩個操作數中只有
ADDR
為內存變量。
4、限制字符限制字符有很多種,有些是與特定體系結構相關,此處僅列出常用的限定字符和i386中可能用到的一些常用的限定符。它們的作用是指示編譯器如何處理其后的C語言變量與指令操作數之間的關系。
5、破壞描述部分破壞描述符用于通知編譯器我們使用了哪些寄存器或內存,由逗號格開的字符串組成,每個字符串描述一種情況,一般是寄存器名;除寄存器外還有“memory”。例如:“%eax”,“%ebx”,“memory”等。
第一個匯編的例子:輸出字符
int main()
{
char const * MESSAGE = "hello world\n";
__asm__ __volatile__ ( "movl $4, %%eax;"
"movl $1, %%ebx;"
"movl %0, %%ecx;"
"movl $12 , %%edx;"
"int $0x80;"
: "=m" (MESSAGE)
);
//printf("hello\n");
return 0;
}
例子說明:
1.首先寫出匯編的標識符:__asm__;然后寫出模板的指令語句,在指令語句中寄存器的的前面要加兩個%,但是在破壞描述部分語句中只用加一個%,具體參加例子2;
2.每條匯編語句用“”括起來,并用;標識語句的結束;語句中的%x標識站位符,由輸入和輸出語句中操作數描述符描述;內嵌匯編可以傳遞最多十個描述符;m標識變量為內存變量,= 標明是輸出操作符;既然MESSAGE為輸入變量,可以在模板中,直接把MESSAGE中的值直接寫到ECX中,修改后的代碼,如下所示:
int main()
{
char const * MESSAGE = "hello world\n";
__asm__ __volatile__ ( "movl $4, %%eax;"
"movl $1, %%ebx;"
"movl $12 , %%edx;"
"int $0x80;"
:
: "c" (MESSAGE)
);
return 0;
}實例代碼2:
/* inline.c */
int main()
{
int a = 10, b = 0;
__asm__ __volatile__("movl %1, %%eax;\\n\\r"
"movl %%eax, %0;"
:"=r"(b) /* 輸出 */
:"r"(a) /* 輸入 */
:"%eax"); /* 不受影響的寄存器 */
printf("Result: %d, %d\\n", a, b);
}代碼理解:
上面的程序完成將變量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)
總結
以上是生活随笔為你收集整理的Linux的gcc可以看汇编,linux gcc 内嵌汇编的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Ext JS权威指南》——2.4节关于
- 下一篇: 数据中心传输需求成以太网市场巨大推动力