内联汇编
- 软件平台:运行于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 传统方式
-
代码如下:
#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; }
-
得到的汇编代码如下:
可以看到,对于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 汇编方式
-
代码如下
#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
-
得到的汇编代码如下:
简短了许多
339 00008580 <add>: 340 8580: 1840 add r0, r0, r1 341 8582: 4770 bx lr
1.2.3 使用内联汇编方式
-
代码如下:
#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; }
-
得到的汇编代码如下:
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方式
-
代码如下:
#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; }
-
得到的汇编代码如下:
可以看到,其生效的语句使用了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}