1. 可变参数列表的实现
GCC 编译器在汇编过程中,调用 C 语言函数传递参数有两种方法:
- 通过堆栈
- 通过寄存器(默认)
若想通过堆栈传递参数,需在定义 C 函数时在函数前加上宏 asmlinkage
asmlinkage int printk(const char *fmt, ...)
正常来讲,函数原型中具有确定的参数类型和数量,保证了函数调用的准确性。
如果在调用函数时,使用不同类型的不同数量的参数进行调用,参数列表的数量和类型对于被调用函数是未知的。
我们就要想办法确定各个可变参数的类型,找到这些可变参数的地址。
头文件 <stdarg.h>
定义了 va_list 类型和用于逐个通过参数列表的三个宏。
被调用函数必须声明一个 va_list 类型的对象,用 va_list 这个指针指向这个可变参数列表的各个参数。
这个 va_list 对象由 va_start(), va_arg() 和 va_end() 使用。
2. 函数调用时的栈结构
C 函数调用时的栈结构:
栈结构 | 说明 |
---|---|
栈底 | 高地址(入栈方向为从高地址到低地址) |
…… | …… |
函数返回地址 | …… |
…… | …… |
函数最后一个可变参数 | 入栈顺序为从右向左 |
…… | …… |
函数第一个可变参数 | 调用 va_start 后 ap 指向这里 |
函数最后一个固定参数 | …… |
…… | …… |
函数第一个固定参数 | …… |
栈顶 | 低地址 |
3. 可变参数列表(第一个可变参数)的地址
我们需要一个基准,这个基准就是可变参数前面固定的那些参数中的最后一个,也即可变参数前面那个参数。
然后以此已知类型的参数为基准,来确定后面的可变参数。
先确定第一个可变参数的地址,方法是调用void va_start(va_list ap, last);
这样,基准有了,就是 last 指定的参数,然后由 last 计算 指向当前可变参数的指针 ap
要找到 ap