欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux内核学习——1. 内联汇编

程序员文章站 2024-03-23 14:20:52
...

内联汇编

  • 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
  • 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链


1、内联汇编

1.1 语法

asm asm-qualifiersasm ( AssemblerTemplate 
                 : OutputOperands 
                 [ : InputOperands
                 [ : Clobbers ] ])

参数解析:

  • asm:内联汇编的标志,可写为 “ asm_” ;

  • asm-qualifiersasm:取值有volatile、 inline、 goto ;

  • AssemblerTemplate:记录汇编指令,如mov %0 &1\n,当有多条的时候可以通过\n分隔;

  • OutputOperands:记录输出操作数,格式为[[符号名]] 约束条件 C语言的变量名

    • 符号名:自己制定,可不写

    • 约束条件:有以下常用的取值

      约束条件 描述
      m memory operand,表示要传入有效的地址,只要 CPU 能支持该地址,就可以传入
      r register operand,寄存器操作数,使用寄存器来保存这些操作数
      i immediate integer operand,表示可以传入一个立即数

      对于这些约束条件,可以加上修饰字符,如"=r"、"+r"、"=&r"

      修饰字符
      = 表示内联汇编会修改这个操作数,即:写
      + 这个操作数即被读,也被写
      & 它是一个 earlyclobber 操作数
    • C语言变量名:自己指定
      例子一[result] "=r" (sum)
      解释:汇编代码通过某个寄存器,把结果写入sum变量中,可通过%[result]引用它;
      例子二"=r"(sum)
      解释:汇编代码可通过“%0”、“%1”引用它(表示第0、1、2…个输入操作数

  • InputOperands:记录输入操作数,格式为[[符号名]] 约束条件 C语言的表达式,与上面类似。

    例子一[a_val]"r"(a),[b_val]"r"(b)

    解释:表示把变量a、b的值放入某些寄存器中,可使用%[a_val]、%[b_val]引用

    例子二"r"(a),"r"(b)

    解释:表示把变量a、b的值放入某些寄存器中,可使用%0、%1引用

  • Clobbers:在输出参数中所设计的需要修改的寄存器、内存等。在这里声明它们会被修改。常用的有

    Clobbers 描述
    cc 表示汇编代码会修改“ flags register"
    memory 表示汇编代码中,除了“ InputOperands”和“ OutputOperands”中指定的之外, 还会会读、写更多的内存

    例子:"r0","r1","r2","r3","memory"

1.2 编写实现加法

1.2.1 传统方式

  1. 代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    
    int add(int a, int b)
    {
    	return a + b;
    }
    
    int main(int argc, char** argv)
    {
    	int a, b, sum;
        
        if (argc != 3)
    	{
    		printf("Usage: %s <val1> <val2>\n", argv[0]);
    		return -1;
    	}
    
    	a = (int)strtol(argv[1], NULL, 0);	
    	b = (int)strtol(argv[2], NULL, 0);	
    
    	printf("%d + %d = %d\n", a, b, add(a, b));
    	return 0;
    }
    
  2. 得到的汇编代码如下:

    可以看到,对于add(),使用了过多的寄存器操作,实际完成效果的只有一到两条。

    321 0000000000400566 <add>:
    322   400566:   55                      push   %rbp
    323   400567:   48 89 e5                mov    %rsp,%rbp
    324   40056a:   89 7d fc                mov    %edi,-0x4(%rbp)
    325   40056d:   89 75 f8                mov    %esi,-0x8(%rbp)
    326   400570:   8b 55 fc                mov    -0x4(%rbp),%edx
    327   400573:   8b 45 f8                mov    -0x8(%rbp),%eax
    328   400576:   01 d0                   add    %edx,%eax	## 实际完成操作
    329   400578:   5d                      pop    %rbp
    330   400579:   c3                      retq
    

1.2.2 汇编方式

  1. 代码如下

    #include <stdio.h>
    #include <stdlib.h>
    
    extern int add(int a, int b);	/* 声明为外部函数 */
    
    int main(int argc, char** argv)
    {
    	int a, b, sum;
        
        if (argc != 3)
    	{
    		printf("Usage: %s <val1> <val2>\n", argv[0]);
    		return -1;
    	}
    
    	a = (int)strtol(argv[1], NULL, 0);	
    	b = (int)strtol(argv[2], NULL, 0);	
    
    	printf("%d + %d = %d\n", a, b, add(a, b));
    	return 0;
    }
    

    add.S文件文件如下:

    .text            // 放在代码段
    .global  add     // 实现全局函数add
    .thumb           // 使用thumb指令, main.c默认使用thumb指令, 所以这里也使用thumb指令
    
    add:
    	add r0, r0, r1
    	bx lr
    
  2. 得到的汇编代码如下:

    简短了许多

    339 00008580 <add>:
    340     8580:   1840        add r0, r0, r1
    341     8582:   4770        bx  lr
    

1.2.3 使用内联汇编方式

  1. 代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    
    int add(int a, int b)
    {
    	int sum;
    
    	__asm__ volatile (
    		"add %0, %1, %2"
    		:"=r"(sum)
    		:"r"(a), "r"(b)
    		:"cc"
    	);
    
    	return sum;
    }
    
    int main(int argc, char** argv)
    {
    	int a, b, sum;
        
        if (argc != 3)
    	{
    		printf("Usage: %s <val1> <val2>\n", argv[0]);
    		return -1;
    	}
    
    	a = (int)strtol(argv[1], NULL, 0);	
    	b = (int)strtol(argv[2], NULL, 0);	
    
    	printf("%d + %d = %d\n", a, b, add(a, b));
    	return 0;
    }
    
  2. 得到的汇编代码如下:

    290 000084c4 <add>:
    291     84c4:   e1a0c00d    mov ip, sp
    292     84c8:   e92dd800    stmdb   sp!, {fp, ip, lr, pc}
    293     84cc:   e24cb004    sub fp, ip, #4  ; 0x4
    294     84d0:   e24dd00c    sub sp, sp, #12 ; 0xc
    295     84d4:   e50b0010    str r0, [fp, #-16]
    296     84d8:   e50b1014    str r1, [fp, #-20]
    297     84dc:   e51b2010    ldr r2, [fp, #-16]
    298     84e0:   e51b3014    ldr r3, [fp, #-20]
    299     84e4:   e0823003    add r3, r2, r3	## -----------------------> 生效效果
    300     84e8:   e50b3018    str r3, [fp, #-24]
    301     84ec:   e51b3018    ldr r3, [fp, #-24]
    302     84f0:   e1a00003    mov r0, r3
    303     84f4:   e24bd00c    sub sp, fp, #12 ; 0xc
    304     84f8:   e89da800    ldmia   sp, {fp, sp, pc}
    

1.2.4 加入earlyclobbe方式

  1. 代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int add(int a, int b)
    {	
        int sum;	
        __asm__ volatile (		
            "add %0, %1, %2"		
            :"=&r"(sum)		
            :"r"(a), "r"(b)		
            :"cc"	
        );	
        
        return sum;
    }
    int main(int argc, char** argv)
    {	
        int a, b, sum;        
        if (argc != 3)	
        {		
            printf("Usage: %s <val1> <val2>\n", argv[0]);		
            return -1;	
        }	
        
        a = (int)strtol(argv[1], NULL, 0);		
        b = (int)strtol(argv[2], NULL, 0);		
        printf("%d + %d = %d\n", a, b, add(a, b));	
        
        return 0;                                                                                         }
    
  2. 得到的汇编代码如下:

    可以看到,其生效的语句使用了3个寄存器进行存储各自的值,这样可以在一定程度上减少使用同一个寄存器存储结果与变量《1.2.3 内联汇编》所带来的问题,即变量修改,结果也修改。

    000084c4 <add>:
        84c4:	e1a0c00d 	mov	ip, sp
        84c8:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
        84cc:	e24cb004 	sub	fp, ip, #4	; 0x4
        84d0:	e24dd00c 	sub	sp, sp, #12	; 0xc
        84d4:	e50b0010 	str	r0, [fp, #-16]
        84d8:	e50b1014 	str	r1, [fp, #-20]
        84dc:	e51b2010 	ldr	r2, [fp, #-16]
        84e0:	e51b3014 	ldr	r3, [fp, #-20]
        84e4:	e0821003 	add	r1, r2, r3	## -----------------------> 生效效果
        84e8:	e1a03001 	mov	r3, r1
        84ec:	e50b3018 	str	r3, [fp, #-24]
        84f0:	e51b3018 	ldr	r3, [fp, #-24]
        84f4:	e1a00003 	mov	r0, r3
        84f8:	e24bd00c 	sub	sp, fp, #12	; 0xc
        84fc:	e89da800 	ldmia	sp, {fp, sp, pc}