Table of Contents
引言
MIPS处理器示例
内联汇编
推荐阅读
没有人愿意编写汇编代码!!!。它很复杂,很难编码,很难调试并且不便于移植。有时,例如,如果性能至关重要,或者我们想使用某些特定的SIMD指令等,我们别无选择。
那么为什么我们需要学习和编写汇编代码呢?
优化–在某些关键点上,我们不希望编译器为我们生成代码消除编译器更改–例如,在关键安全系统中,我们不希望编译器为我们生成代码,因为我们需要对生成的代码进行完全控制特殊的CPU指令–编译器不使用某些指令,例如软件中断,同步指令,屏障等。如果要使用这些指令,我们必须编写汇编代码如果要混合使用C和Assembly,可以使用以下2种方法:
单独的文件– .c文件中的C源代码,.s文件中的汇编代码内联汇编–将C和汇编混合在同一.c文件中例如,我们要在ARM处理器程序集中创建一个函数,我们将编写以下fn.s文件:
.globl addfn; addfn: add r0,r0,r1; mov pc,lr;上面的文件声明一个简单的函数,它接受2个参数并返回它们的和。参数存储在r0,r1通用寄存器中,返回值存储在r0中
然后我们将编写使用该函数的C代码
#include <stdio.h> #include <stdlib.h> extern int addfn(int,int); int main(void) { int res; res=addfn(10,20); printf("res=%d\n",res); /* prints !!!Hello World!!! */ return EXIT_SUCCESS; }我们需要了解有关ARM中C编译器的一些详细信息:
最多4个参数,编译器使用寄存器r0,r1,r2,r3如果我们使用4个以上的参数,则第5个以上的参数将存储在堆栈中返回值存储在r0中编译代码:
arm-none-linux-gnueabi-gcc -o app1 ./test.c ./addfn.s
汇编代码的主要问题是平台依赖性–如果我们要从一种架构切换到另一种架构,则需要重写代码。编译器也有不同的规则来使用寄存器,堆栈等。
例如,在MIPS上编写相同的简单函数:
.globl addfn; .type addfn, @function; .ent addfn addfn: add $a0,$a0,$a1; move $v0,$a0; .end addfn; jr $ra;MIPS上的C编译器将寄存器a0,a1,a2,a3用于函数参数并将寄存器v0用于返回值
编写内联汇编意味着在同一个文件上编写C +汇编,并编译两段代码。它可以用于优化,但是我们使用此代码的主要原因是生成从未由编译器生成的指令。例如,如果我们要在linux中编写系统调用-函数以使用某些内核服务,则需要使用软件中断指令将处理器状态从用户模式更改为特权模式-编译器永远不会生成此指令,因此我们需要这样做手动地
在此示例中,我们编写系统调用getpid():
#include <stdio.h> #include <stdlib.h> #include <sys/syscall.h> #include <unistd.h> int mygetpid() { int res; asm volatile ("movl $20,%