AT&T匯編語(yǔ)法
GCC只支持AT&T匯編語(yǔ)法內(nèi)嵌在C語(yǔ)言中。
Intel和AT&T匯編風(fēng)格對(duì)比:

AT&T尋址
寄存器間接尋址:
- mov (%eax), %ebx ;表示將地址eax所指的內(nèi)存復(fù)制4字節(jié)到ebx
寄存器相對(duì)尋址:
- movb -4(%eax), %al; 表示將地址(eax - 4)所指的內(nèi)存復(fù)制一字節(jié)到al
變址尋址:
segreg(段基址):base_address(offset_address,index,size)
對(duì)應(yīng)表達(dá)式為:
segreg(段基址):base_address + offset_address + index * size
格式中不存在的部分要用‘,’占位,共有4種組合:
-
無base_address,無offset_address
movl %eax, (,%esi,2) -- 表示將eax的值寫入
esi*2所指的內(nèi)存 -
無base_address,有offset_address
movl %eax,(%ebx,%esi,2) -- 表示將eax的值寫入
ebx + esi*2所指的內(nèi)存 -
有base_address,無offset_address
movl %eax, base_value(,%esi,2) -- 表示將eax的值寫入base_value + esi*2`所指的內(nèi)存
-
有base_address,有offset_address
movl %eax, base_value(%ebx,%esi,2) -- 表示將eax的值寫入base_value + %ebx + esi*2`所指的內(nèi)存
基本內(nèi)聯(lián)匯編
內(nèi)聯(lián)匯編基本格式:
asm [volatile] ("assembly code") volatile關(guān)鍵字可選,使得gcc在-O指定優(yōu)化時(shí),不改變匯編代碼
assembly code規(guī)則:
- 指令必須在雙引號(hào)內(nèi)。
- 指令之間用分號(hào)、換行符
\n或者換行符加制表符\n\t分隔
示例:
char* str = "hello,world\n";
int count = 0;
void main(){
asm volatile ("\
pusha; \
movl $4, %eax; \
movl $1, %ebx; \
movl str, %ecx; \
movl $12, %edx; \
int $0x80; \
movl %eax, count; \
popa \
");
}
擴(kuò)展內(nèi)聯(lián)匯編
當(dāng)匯編代碼嵌入到C代碼中,如何找到可用的寄存器是個(gè)問題,程序員不知道哪些寄存器已經(jīng)被分配。GCC提供了擴(kuò)展內(nèi)聯(lián)匯編格式。
擴(kuò)展內(nèi)聯(lián)匯編格式:
asm [volatile] ("assembly code" : output : input : clobber/modify)
- output : <output> 用來指定匯編代碼的數(shù)據(jù)如何傳遞給C代碼使用,output中每個(gè)操作數(shù)的格式為:
"操作數(shù)修飾符 + 約束名"(C變量名)
引號(hào)和圓括號(hào)不可少,操作數(shù)修飾符通常為‘=’。多個(gè)操作數(shù)之間用‘,’分隔
- input : <input> 用來指定C中數(shù)據(jù)如何傳遞給匯編使用,input中每個(gè)操作數(shù)的格式為:
”[操作數(shù)修飾符] + 約束名“(C變量名)
引號(hào)和圓括號(hào)不可少,操作數(shù)修飾符為可選項(xiàng)。多個(gè)操作數(shù)之間用‘,’分隔
clobber/modify : 匯編代碼執(zhí)行后會(huì)破壞一些內(nèi)存或寄存器資源,通過此項(xiàng)通知編譯器,哪些寄存器或內(nèi)存需要提前保護(hù)起來。
-
約束名
寄存器約束
寄存器約束就是要求gcc使用哪個(gè)寄存器
a: eax/ax/al b: ebx/bx/bl c: ecx/cx/cl d: edx/dx/dl D: edi/di S: esi/si q: eax/ebx/ecx/edx中任意一個(gè) r: eax/ebx/ecx/edx/esi/edi中任意一個(gè) g: 表示存放到任意地點(diǎn)(寄存器和內(nèi)存) A:把eax和edx組合成64位整數(shù) f: 表示浮點(diǎn)寄存器 t: 表示第1個(gè)浮點(diǎn)寄存器 u: 表示第2個(gè)浮點(diǎn)寄存器示例:
#include <stdio.h>
void main(){
int in_a = 1, in_b = 2, out_sum;
asm ("addl %%ebx, %%eax":"=a"(out_sum):"a"(in_a),"b"(in_b));
printf("sum = %d\n",out_sum);
}
ps: 擴(kuò)展內(nèi)聯(lián)匯編中寄存器前綴是兩個(gè)%
內(nèi)存約束
m: 表示操作數(shù)可以使用任意一種內(nèi)存形式
o: 操作數(shù)位內(nèi)存變量
示例 :
#include <stdio.h>
void main(){
int in_a = 1, in_b = 2;
asm ("movb %b0, %1"::"a"(in_a),"m"(in_b));
printf("in_b = %d\n",in_b);
}
%1序號(hào)占位符,代表in_b的內(nèi)存地址(指針)
%b0表示對(duì)寄存器eax的引用,b表示一個(gè)字節(jié),所以%b0表示al寄存器
立即數(shù)約束
只放在input中
i: 整數(shù)立即數(shù)
F: 浮點(diǎn)數(shù)立即數(shù)
I: 0-31之間的立即數(shù)
J: 0-63之間的立即數(shù)
N: 0-255之間的立即數(shù)
O: 0-32之間的立即數(shù)
X: 任何類型的立即數(shù)
通用約束
0-9: 只用在input部分,表示與output和input中第n個(gè)操作數(shù)用相同的寄存器或內(nèi)存
-
占位符
為方便對(duì)操作數(shù)的引用,擴(kuò)展內(nèi)聯(lián)匯編提供了占位符來表示指定操作數(shù)。
序號(hào)占位符
序號(hào)占位符是對(duì)在output和input中的操作數(shù),按照他們從左到右出現(xiàn)的次序從0開始編號(hào),一直到9,最多支持10個(gè)序號(hào)占位符。引用它的格式是%0-9
若%0表示eax,則%h0 -- 表示ah,%b0表示al
名稱占位符
可以對(duì)操作數(shù)進(jìn)行顯示命名,格式如下:
[名稱]”約束名“(c變量)
采用%[名稱]的形式引用操作數(shù)示例:
#include <stdio.h> void main(){ int in_a = 18, in_b = 3, out = 0; asm ("divb %[divisor]; movb %b1, %[result]":[result]"=m"(out):"a"(in_a),[divisor]"m"(in_b)); printf("out = %d\n",out); } -
操作數(shù)修飾符
在output中:
=: 表示操作數(shù)只寫 +: 表示操作數(shù)可讀寫 &: 表示此output中的操作數(shù)要獨(dú)占約束(分配的)寄存器,只供output使用,任何input中分配的寄存器不能與此相同input中:
%: 該操作數(shù)可以和下一個(gè)輸入操作數(shù)交換一般情況下input中的C變量是只讀的,output中的C變量是只寫的。
示例:
#include <stdio.h> void main(){ int in_a = 1, in_b = 2; asm ("addl %%ebx, %%eax":"+a"(in_a):"b"(in_b)); printf("sum = %d\n",in_a); }參考
《操作系統(tǒng)真相還原》