GNU内联汇编
内联汇编即在C中直接使用汇编语句进行编程,使程序可以在C程序中实现C语言不能完成的一些工作,例如,在下面几种情况中必须使用内联汇编或嵌入型汇编
- 程序中使用饱和算术运算(Saturating Arithmetic)
- 程序需要对协处理器进行操作
- 在C程序中完成对程序状态寄存器的操作
__asm__ __volatile__("asm code":output:input:changed registers);
Note:
- 使用
__asm__
和__volatile__
表示编译器将不检查后面的内容,而是直接交给汇编器。 - 如果希望变压器你优化,
__volatile__
可以不加 - 没有asm code也不能省略
""
- 没有前面的和中间的部分,不可以相应的省略
:
- 没有changed 部分,必须相应的省略
:
- 最后的
;
不能省略,对于C语言来说这是一条语句 - 汇编代码必须放在一个字符串内,且字符串中间不能直接按回车换行,可以写成多个字符串,注意中间不能有任何符号,这样就会将两个字符串合并为一个
- 指令之间必须要换行,还可以使用
\t
使指令在汇编中保持整齐
asm code
"mov r0, r0\n\t"
"mov r1,r1\n\t"
"mov r2,r2"
output(asm->C)
:"constraint" (variable)
"constraint"
用于定义variable的存放位置:r
表示使用任何可用的寄存器m
表示使用变量的内存地址+
可读可写=
只写&
表示该输出操作数不能使用输入部分使用过的寄存器,只能用"+&"
或"=&"
的方式使用
input(C->asm)
:"constraint" (variable/immediate)
"constraint"
用于定义variable的存放位置:r
表示使用任何可用的寄存器(立即数和变量都可以)m
表示使用变量的内存地址i
表示使用立即数
例子
int a=100,b=200;
int result;
__asm__ __volatile__(
"mov %0,%3\n\t" //%0是一个占位符,表示result,之后的类推
"ldr r0,%1\n\t"
"ldr r1,%2\n\t"
"str r0,%2\n\t"
"str %1,%1\n\t"
:"=r"(result),"+m"(a),"+m"(b)
:"i"(123)
);
ATPCS
- 子程序间通过寄存器R0~R3来传递参数,如果参数多于四个,则多出的部分用堆栈传递,被调用的子程序在返回前无须恢复寄存器R0~R3的内容
- 在子程序中,使用寄存器R4~R11来保存局部变量,如果在子程序中使用到了R4~R11中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作,在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量
- R12用作子程序间scrtach寄存器(用于保存SP,在函数返回时使用该寄存器出栈),记作ip
- R13用作数据栈指针,记作sp
- R14用作连接寄存器,记作lr
- R15记作程序寄存器,记作pc
相互调用
C和汇编相互调用要特别注意遵守相应的ATPCS规则
C调用汇编
//.c
#include <stdio.h>
extern void strcopy(char* des, const char* src);
int main(){
const char* srcstr = "src string";
char desstr[]="des string";
strcopy(desstr, srcstr);
return 0;
}
;.asm
.global strcopy
strcopy: ;R0指向目的字符串
;R1指向源字符串
LDRB R2, [R1], #1 ;加载字节并更新源字符串指针地址
STRB R2, [R0], #1 ;存储季节并更新目的字符串指针地址
CMP R2, #0 ;判断是否为字符串结尾
BNE strcopy ;如果不是,程序跳转到strcopy继续循环
MOV pc, ir ;程序返回
汇编调用C
//.c
int fcn(int a, int b , int c, int d, int e){
return a+b+c+d+e;
}
;.asm
;假设程序进入f时,R0中的值为i
;int f(int i){return fcn(i, 2*i, 3*i, 4*i, 5*i);}
.text
.global _start
_start:
STR lr, [sp, #-4]! ;保存返回地址lr
ADD R1, R0, R0 ;计算2*i(第2个参数)
ADD R2, R1, R0 ;计算3*i(第3个参数)
ADD R3, R1, R2 ;计算5*i
STR R3, [SP, #-4]! ;第5个参数通过堆栈传递
ADD R3, R1, R1 ;计算4*i(第4个参数)
BL fcn ;调用C程序
ADD sp, sp, #4 ;从堆栈中删除第五个参数
.end