參考資料:《Linux內核完全剖析》,《新版匯編語言程序設計》,《Linux C編程一站式學習》
最近要改個C語言算法的關鍵部分用匯編語言實現,Linux裡嵌入匯編基本使用AT&T匯編,比如Linux系統的啟動部分用的就是AT&T匯編 。以前學過AT&T匯編,但學過一段時間就忘了,但對Intel匯編基礎比較熟悉,兩者使用方法基本相似,所以對著Intel匯編,花點時間看AT&T匯編也就容易了。
下面看一下兩者的區別,然後給出Linux語言中嵌入AT&T匯編的具體的例子。
一、AT&T匯編和Intel匯編主要區別
1,兩者源和目的操作數次序相反。 AT&T的源和目的是從左到右,並且其寄存器前要加“%”;Intel的是右到左,不需要加"%"。
例如:AT&T: movl %ecx, %eax (ecx為源操作數,eax為目的操作數)
Intel: mov dx, bx (bx為源操作數,dx為目的操作數
2,AT&T立即操作數前需要加"$";Intel的不用
例如:AT&T:movl $2, %eax
Intel:mov ax, 2
3,AT&T中內存操作的長度由操作碼最後一個字符來確定。"b","w","l"分別表示內存引用為1字節8位,2字節16位,4字節32位。
Intel使用操作前綴byte ptr, word ptr, dword ptr
例如:AT&T:movl %ecx, %eax
Intel:mov al, byte ptr ttt
這三點是最重要的區別,除此之外還有跳轉/調用時不太一樣。
二、嵌入匯編基本格式
asm("匯編語句"
"輸出寄存器"
"輸入寄存器"
"會被修改的寄存器"//告訴編譯器在執行這條__asm__語句時這裡指定的寄存器要被改寫,所以在此期間不要用這裡的寄存器保存其它值。)
//asm.c
#include <stdio.h>
unsigned int LeftShift(unsigned int uiNumber, unsigned char iBits)
{
register unsigned int _res;
__asm__("rol %1, %%eax;"
:"=a" (_res)
:"c" (iBits),"0"(uiNumber)
);
return _res;
}
int main(void)
{
unsigned int ret = 0;
ret = LeftShift(4, 2);
printf("1,ret:%u\n", ret);
return 0;
}
[root@ www.linuxidc.com]# gcc asm.c -o app
[root@ www.linuxidc.com]# ./app
1,ret:16
以上給出一個循環左移的例子,不關注循環左移指信rol本身,只關注AT&T匯編用法。
首先定義了個寄存器變量_res,用其保存返回值。
因為AT&T匯編使用寄存器,其前面需要帶“%”,而在c語言中“%”是個特殊格式字符,所以需要兩個百分號“%%”才最終表示一個“%”,這個和轉義字符有點類似。
:"=a"(_res)表示用寄存器eax的值輸出給_res,用%0表示此寄存器,
:"c" (iBits),"0"(uiNumber),c表示輸入寄器ecx,0表示使用上面的輸出寄存器eax,所以ecx和eax分別保存iBits和uiNumber變量值。指令中的%1指寄存器ecx,所以其編號規則是從“輸出寄器”開始從左到右,從上到下進行的。
可以發現rol %1,%%eax其實可以直接寫成:rol %1, %0