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

内联汇编

  • 软件平台:运行于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语言的变量名

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

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

      约束条件描述
      mmemory operand,表示要传入有效的地址,只要 CPU 能支持该地址,就可以传入
      rregister operand,寄存器操作数,使用寄存器来保存这些操作数
      iimmediate 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}
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值