GCC 行內匯編
在MIT6.828的實驗中,有幾處用到了很底層的函式,都以行內匯編的形式存在,例如
static inline uint32_t
read_esp(void)
{
uint32_t esp;
asm volatile("movl %%esp,%0" : "=r" (esp));
return esp;
}
static inline uint32_t
read_ebp(void)
{
uint32_t ebp;
asm volatile("movl %%ebp,%0" : "=r" (ebp));
return ebp;
}
因此這篇博客對于行內匯編的基本用法做一個總結,
首先是一般的形式,只能使用全域變數來傳遞資料,例如如下程式(插入在kern/monitor.c):
uint32_t test_val=0;
int mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
uint32_t *ebp=(uint32_t*)read_ebp();
cprintf("the ebp provided is :%8x and eip is %8x\n",ebp,ebp[1]);
// you could use simple asm if you use global variables
asm volatile(
"push %eax \n"
"mov %ebp,%eax \n"
"mov %eax,test_val \n"
"popl %eax \n"
);
cprintf("my ebp is %8x\n",(uint32_t*)test_val);
return 0;
}
運行結果如下

這里就跟普通的匯編一樣的使用方式,注意關鍵字volatile是為了防止被優化,還有每行匯編陳述句后面的\n,又或者如這段程式
int32_t eip=0;
int main()
{
//basic inline assembly
//global variable can be use
asm volatile(
"push %eax \n"
"call .testpop \n"
".testpop: \n"
"pop %ebx \n"
"movl %ebx,%eax \n"
"movl %eax,eip \n"
"pop %eax \n"
);
printf("the eip is :%x\n",eip);
asm volatile(
"push %eax \n"
"movl %ebp,%eax \n"
"movl %eax,eip \n"
"pop %eax \n"
);
printf("the ebp is :%x\n",eip);
return 0;
}
如果只能使用全域變數,必然會有很多不方便,為了能使用區域變數,需要使用擴展的行內匯編,擴展的行內匯編形式如下
asm("assembly code"
:output location
:input orperands
:changed registers
);
其中
output location :輸出的放哪兒
input operands :哪些輸入
changed registers:改變了哪些暫存器
并且輸入和輸出部分都是如下形式
"constraint"(variable)
約束符主要是限制暫存器的使用
a use %eax %ax or %al
b use %ebx,%bx,or %bl
c use %ecx,%cx,or %cl
d use %edx,%dx,or %dl
S use %esi or %si
D use %edi or %si
r use any available register
q use one of %eax,%ebx,%ecx or %edx
A use %eax&%edx for 64-bit value
f use float register
m use memory location of variable
同時可以加上修飾符
+ read & write
= only write
來看一段程式
uint32_t ebp;
__asm__ __volatile__(
"push %%eax \n\t"
"mov %%ebp,%%eax \n\t"
"mov %%eax,%%edx \n\t"
"pop %%eax "
:"=d"(ebp)
:
:"%eax"
);
printf("the ebp is %8x\n",(uint32_t*)ebp);
這里"=d"(ebp)意思是輸出使用暫存器%edx,并且把結果放到變數ebp中,沒有輸入所以省略,但是冒號不能省,這個程序改變了%eax,注意的是,編譯器默認輸入輸出中涉及的暫存器都被改變,因此不能再將這部分暫存器寫到改變部分去,注意匯編代碼中的暫存器%eax要寫成%%eax,每條陳述句完要寫\n\t,
再來看一段程式
int xa=6;
int xb=2;
int result_1;
__asm__ __volatile__(
"add %%ebx,%%eax \n\t"
"movl $2,%%ecx \n\t"
"mul %%ecx \n\t"
"movl %%eax,%%edi \n\t"
"movl %%eax,%%edx"
:"=d"(result_1)
:"a"(xa),"b"(xb)
:"%ecx","%edi"
);
printf("the result is %d\n",result_1);
:"=d"(result_1)輸出使用%edx,放到result_1這個變數中;
:"a"(xa),"b"(xb)輸入變數xa的值放到%eax中,xb的值放到%ebx中;
:"%ecx","%edi"這個程序還改變了%ecx和%edi,
再來看一個例子
int data1=10;
int data2=20;
int result;
__asm__ __volatile__(
"imul %%edx ,%%ecx \n\t"
"movl %%ecx ,%%eax \n\t"
:"=a"(result)
:"d"(data1),"c"(data2)
);
printf("10*20 is %d\n",result);
有了上面的例子,這個應該就很好理解了,然而在我們看別人寫的行內匯編中,有時會出現%0,%1這種,這叫占位符,就是代表第幾個運算元所在的暫存器,例如看如下代碼
// %0 is the register to store result
// %1 is the register to store data1
// %2 is the register to store data2
__asm__ __volatile__(
"imul %1 ,%2 \n\t"
"movl %2 ,%0 \n\t"
:"=r"(result)
:"r"(data1),"r"(data2)
);
printf("10*20 is %d\n",result);
這里使用了限定符r就是,讓編譯器自己選擇可用的暫存器,注意這里改變的暫存器串列為空,需要連帶冒號一起省略,
同時,也可以用輸入變數來接受結果,結合占位符,有如下代碼
__asm__ __volatile__(
"imul %1 ,%0 \n\t"
:"=r"(data2)
:"r"(data1),"0"(data2)
);
輸入和輸出都是data2,
但是,如果輸入輸出過多,還用數字就會顯得不太好,因此gcc也有一個方便的做法
data1=10;
data2=20;
__asm__ __volatile__(
"imul %[value1] ,%[value2] \n\t"
:[value2]"=r"(data2)
:[value1]"r"(data1),"0"(data2)
);
printf("10*20 is %d\n",data2);
全文的測驗代碼如下:
//test_asm.c
#include <stdio.h>
#include <stdint.h>
int32_t eip=0;
int main()
{
//basic inline assembly
asm volatile(
"push %eax \n"
"call .testpop \n"
".testpop: \n"
"pop %ebx \n"
"movl %ebx,%eax\n"
"movl %eax,eip \n"
"pop %eax \n"
);
printf("the eip is :%x\n",eip);
asm volatile(
"push %eax \n"
"movl %ebp,%eax\n"
"movl %eax,eip \n"
"pop %eax \n"
);
printf("the ebp is :%x\n",eip);
uint32_t ebp;
__asm__ __volatile__(
"push %%eax \n\t"
"mov %%ebp,%%eax \n\t"
"mov %%eax,%%edx \n\t"
"pop %%eax "
:"=d"(ebp)
:
:"%eax"
);
printf("the ebp is %8x\n",(uint32_t*)ebp);
int xa=6;
int xb=2;
int result_1;
__asm__ __volatile__(
"add %%ebx,%%eax \n\t"
"movl $2,%%ecx \n\t"
"mul %%ecx \n\t"
"movl %%eax,%%edi \n\t"
"movl %%eax,%%edx"
:"=d"(result_1)
:"a"(xa),"b"(xb)
:"%ecx","%edi"
);
printf("the result is %d\n",result_1);
int data1=10;
int data2=20;
int result;
__asm__ __volatile__(
"imul %%edx ,%%ecx \n\t"
"movl %%ecx ,%%eax \n\t"
:"=a"(result)
:"d"(data1),"c"(data2)
);
printf("10*20 is %d\n",result);
__asm__ __volatile__(
"imul %1 ,%2 \n\t"
"movl %2 ,%0 \n\t"
:"=r"(result)
:"r"(data1),"r"(data2)
);
printf("10*20 is %d\n",result);
// you could refer them
// 0 means use the first register to store the input and output
__asm__ __volatile__(
"imul %1 ,%0 \n\t"
:"=r"(data2)
:"r"(data1),"0"(data2)
);
printf("10*20 is %d\n",data2);
// you could rename
// [name] "constraint"(variable)
data1=10;
data2=20;
__asm__ __volatile__(
"imul %[value1] ,%[value2] \n\t"
:[value2]"=r"(data2)
:[value1]"r"(data1),"0"(data2)
);
printf("10*20 is %d\n",data2);
return 0;
}
編譯指令: gcc -O0 -m32 test_asm.c -o test
結果如下

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/308196.html
標籤:C
上一篇:第六階段_typedef關鍵字詳解:創建一個classroom的結構體用來保存一個班級 的資訊,所有學生的資訊,以及班級人數
