1 在汇编中使用结构体
首先考虑一下结构体:
struct test
{
int a;
int b;
}
这个结构体很简单,只是用来举例子。在c语言中,如果我们要访问结构体中的某个成员,这非常容易:
struct test t;
t.a = 1;
假如我们想知道结构体中某个成员在结构体中的偏移,这也不难,比如linux kernel就提供了offsetof
宏来实现这个功能:
offsetof(struct test, b) /* 得到的就是成员b在结构体中的偏移 */
这一切在c语言中都平平无奇,但是考虑这样一个需求:我们需要在汇编语言中使用结构体。这时候怎么办呢?汇编文件里用c的语法定义结构体肯定是不行的,现实的做法是,把结构体的首地址存放在某个寄存器里,然后只要知道各个成员的偏移,就可以通过首地址+偏移
的方式寻址到某个具体的成员。
于是,问题的关键来了。我们怎么获取到各个成员的偏移呢?仍旧使用offsetof
宏肯定是不行的,汇编器根本不认识c的语法。最容易想到的方法就是手工算好偏移,然后用于汇编,考虑到程序的可读性,这个偏移用宏定义的形式给出:
#define A_OFFSET (0)
#define B_OFFSET (4)
但这样就是硬编码,万一结构体的成员有变动,那维护起来很麻烦。我们期待的是类似offsetof
宏那样的解决方法,不管结构体怎么变,都让编译器帮我们计算好偏移,这样偏移总是正确的,但这在汇编里面不可行。对于这一矛盾,kernel的解决办法是,在c文件
中使用offsetof
算出偏移量,然后想办法自动化 的把算出来的偏移量表示成宏定义,之后再汇编里包含相应头文件。这样,汇编文件直接使用宏定义就可以总是正确的得到结构体中成员的偏移量。如此一来,在汇编中使用结构体,让数据组织的更有结构,就完全可行了。在kernel中实现上述功能的c文件
通常被命名为asm-offsets.c
,自动生成的表示偏移量的宏被存放在自动生成的头文件中,这个头文件被命名为asm-offsets.h
。
那么这一切到底是怎么做到的呢?这就不得不提asm-offsets.c
使用到的一个宏DEFINE
,这个宏是自动生成asm-offsets.h
的关键。
2 DEFINE宏
首先给出DEFINE
的定义:
#define DEFINE(sym, val) \
asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val))
这真是好奇怪的一个宏,虽然内联汇编并不稀罕,但这样的内联汇编确实少见。这个宏在asm-offsets.c
中是这么使用的,假如我需要上文的struct test
的成员的偏移,那么需要在asm-offsets.c
中写出如下代码:
int main(void)
{
DEFINE(TEST_A, offsetof(struct test, a));
DEFINE(TEST_B, offsetof(struct test, b));
}
接下来,将asm-offsets.c
编译成asm-offsets.s
,注意这里的编译是狭义上的编译,即只编译不汇编。仍旧以本例来说,编译后,asm-offsets.s
的内容(只选其中关键内容,不是所有内容)如下:
.ascii "->TEST_A #0 offsetof(struct test, a)"
.ascii "->TEST_B #4 offsetof(struct test, b)"
可以看到,上述内容已经包含了宏名
以及偏移量(#0、#4)
,但还不是一个合法的c语言宏定义的形式。接下来怎么得到asm-offsets.h
呢?kernel采取的做法很直接,直接使用sed
、echo
等工具,来匹配文本,处理文本,并生成相应的asm-offsets.h
。更细一点说,从上述文本中提取出三部分内容:
作为宏名:TEST_A
作为偏移:0
作为注释:offsetof(struct test, a)
作为宏名:TEST_B
作为偏移:4
作为注释:offsetof(struct test, b)
最终生成的asm-offsets.h
大体如下:
#define TEST_A 0 /* offsetof(struct test, a) */
#define TEST_B 4 /* offsetof(struct test, b) */
当然,除了结构体中的成员偏移,我们还可以用DEFINE
来定义供汇编文件使用 的表示结构体大小的宏,如:
DEFINE(TEST_SIZE, sizeof(struct test));
DEFINE
外加后续的处理只是机制,只要机制有了,就可以玩出花来。要是没有机制呢?kernel开发者就会想办法把机制构建出来。
参考文献
[1] What’s purpose of “arm64/kernel/asm-offsets.c”?
[2] lib/asm-offsets.c的作用