APCS 全称:ARM 过程调用标准 如果要写用来与编译后的 C 连接的汇编代码,则必须使用 APCS。
今天的课程最终的两个目标:使用符合APCS标准的汇编写输出hello world 以及编写container_of宏 。这两个的推导过程比较复杂和具有跳跃性。结论的话要记住两个知识点,一:编写汇编函数的时候要对相应的寄存器进行保护。保护代码需要记住,每次写函数的时候都要用到。二:container_of实现了根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针的功能。以后在linux内核代码出现要知道意思,并明白推导过程。
最后代码直接看都是不好看懂的。所以我也贴出推导的前一个代码。
1 #include<stdio.h> 2 3 __asm__( 4 5 "sum :\n" //保护寄存器,sp pc 6 "sub sp,sp,#8\n" 7 "str lr,[sp,#4]\n" 8 "str fp,[sp]\n" 9 "add fp,sp,#4\n" 10 11 "mov lr,pc\n" 12 "b hello\n" 13 14 "sub sp,fp,#4\n" 15 "ldr fp,[sp]\n" 16 "ldr lr ,[sp + 4]\n" 17 "add sp,sp,#8\n" 18 "mov pc,lr\n" 19 ); 20 __asm__( 21 "hello:\n" 22 "mov pc,lr\n“ 23 24 ); 25 int main() 26 { 27 __asm__ __volatile__( 28 : 29 : 30 : "r0","r1","memory" 31 ); 32 printf("welcome back\n"); 33 34 }
上面的函数中新的知识点比较多,我这里列出我认为重要的。前面说过,汇编中有R0到R15个寄存器。这16个寄存器有着另一套的命名方式。
a1~a4对应R0~R3 作为函数的输入参数。其中R0又是返回值。V1~V6对于对应R4~R9,作为局部变量用。fp对应R11 初始化帧指针寄存器 ip 对应R12 内部过程调用寄存器 ,sp对应r13 栈指针。lr 对应R14 ,链接寄存器 pc对应R15 程序计数器 。
整个代码就是建立栈的回溯过程。 这里的栈有四种情况,栈是向上还是向下。栈顶的指针存不存值。APCS的内容比较多,以后用到了再细看。先记住这些知识点。
上面的代码直接用汇编写的话,文件后zui名为.s 或.S. 简化版本如下,以后记得写汇编函数加入这3~5行和后11~12行。
1 .global main 2 main: 3 mov ip,sp 4 stmfd sp! ,{fp,ip,lr} 5 sub fp,ip,#4 6 7 ldr r0,=he 8 mov lr,pc 9 b printf 10 11 sub sp,fp,#8 12 ldmfd sp,{fp,sp,pc} 13 14 he: 15 .asciz "hello world\n" 16 .align 2
container_of在linux内核中使用到。看到这个宏不由感叹。代码的艺术性啊,太精妙了。
1 #include<stdio.h> 2 3 struct person{ 4 int age; 5 }; 6 struct man{ 7 int len; 8 int h; 9 char a; 10 struct person p; 11 int s; 12 int q; 13 }; 14 int main() 15 { 16 struct man tom; 17 tom.p.age = 20; 18 tom.len = 12; 19 struct person *head = &tom.p; 20 struct man tmp; 21 int x = 0; 22 x = (unsigned long)&tmp.p - (unsigned long)&tmp; 23 struct man *m = (struct man *)((unsigned long)head - x); 24 printf("tom‘s len is %d\n",m->len); 25 }
上面的代码已经能实现根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针的功能。但是最后的代码更为精妙:
1 #include<stdio.h> 2 #define container_of(ptr,type,mem) (type*)((unsigned long)ptr - (unsigned long)&((type *)0)->mem) 3 struct person{ 4 int age; 5 }; 6 struct man{ 7 int len; 8 int h; 9 char a; 10 struct person p; 11 int s; 12 int q; 13 }; 14 int main() 15 { 16 struct man tom; 17 tom.p.age = 20; 18 tom.len = 12; 19 struct person *head = &tom.p; 20 struct man *m =container_of(head,struct man,p); 21 printf("tom‘s len is %d\n",m->len); 22 }