第十七天:APCS和container_of宏

  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 }

    

时间: 2024-10-01 02:42:19

第十七天:APCS和container_of宏的相关文章

深入浅出实例解析linux内核container_of宏

做一件事情首先应该知道它的目的是什么. container_of的目的:如何通过结构中的某个变量获取结构本身的指针. 总体思路:假想一下,你的结构体中有好几个成员,你如何通过里面的"任一成员"获取整个结构体的首地址呢.container_of的做法就是通过typeof定义一个与"任一成员"同类型的指针变量pvar_a(假设变量名就是pvar_a),并让指针变量pvar_a指向这个"任一成员",然后用 "pvar_a的地址" 减

C语言之offset_of宏和container_of宏

通过结构体整体变量来访问其中各个元素,本质上是通过指针方式来访问的,形式上是通过.的方式来访问的(这时候其实是编译器帮我们自动计算了偏移量). 1:offset_of宏 作用:计算结构体中某个元素和结构体首地址的偏移量(其实质是通过编译器来帮我们计算). 定义: #define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER) 参数分析:TYPE是结构体类型,MEMBER是结构体中一个元素的元素名 返回值:member元素相对于整个结构体

C语言之offsetof宏和container_of宏

首先我们要明白一点通过结构体变量来访问结构体中的各个元素时,其本质上是 通过指针的方式来实现访问的,只不过是这个时候编译器帮自动帮我们计算了每个 元素与结构体起始地址之间的偏移量而已 一:offsetof宏: #define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER) 1:参数与返回值分析: (1)TYPE是结构体类型,MEMBER是结构体中一个元素的元素名 (2)这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,

container_of宏定义解析

container_of宏,定义kernel.h中: /** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within th

对offsetof、 container_of宏和结构体的理解

offsetof 宏 #include<stdio.h> #define offsetoff(type, member)      ((int)&((type*)0)->member) /* ((type*)0)->member 释义:声明一个相应类型的结构体指针,该指针指向0地址处.再通过该指针访问各元素.我们只要获取一个指针就能访问其内部的元素吗,可以这么搞?其实我是想联系全局和非全局变量,通过上面这个代码也许你不明白我要表达的意思.请继续慢慢看,直到本文后面,结合本文

container_of宏剖析

container_of宏剖析//该宏位于include/linux/kernel.h 1.定义格式 /** * container_of - cast a member of a structure out to the containing structure * * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member:the nam

container_of()宏

最近学习为Android添加编写内核驱动,遇到的第一个问题就是linux中大名鼎鼎的container_of()宏. container_of()是Linux内核中一个非常重要的宏,主要功能就是:根据结构体中一个域成员的地址获取整个结构体的地址. container_of()定义如下: 1 /** 2 * container_of - cast a member of a structure out to the containing structure 3 * @ptr: the pointe

linux中offsetof与container_of宏定义

linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->MEMBER) /** * container_of - cast a member of a structure out to the containing structure * @ptr:        the pointer to the member. * @type:       the type

offsetof与container_of宏分析

offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) (TYPE *)0非常巧妙,告诉编译器有一个指向结构体 TYPE 的指针,其地址是0,然后取该指针的 MEMBER 地址 &((TYPE *)0)->MEMBER,因为基址是0,所以这时获取到的 MEMBER