天天看點

GCC 内聯彙編基礎

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;
}

           

運作結果如下

GCC 内聯彙編基礎

這裡就跟普通的彙編一樣的使用方式,注意關鍵字

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;
}


           

繼續閱讀