Kernel里面用得最多的printk()函数为例子:
函数参数由 右到左依次压栈,右边变量处于高地址。因此,在printk()函数内部,上面参数在栈中的位置为:
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int r;
#ifdef CONFIG_KGDB_KDB
if (unlikely(kdb_trap_printk)) {
va_start(args, fmt);
r = vkdb_printf(fmt, args);
va_end(args);
return r;
}
#endif
va_start(args, fmt);
r = vprintk(fmt, args);
va_end(args);
return r;
}
在代码里面调用printk()进行打印:
printk("%s\n", "hello world");
函数参数由 右到左依次压栈,右边变量处于高地址。因此,在printk()函数内部,上面参数在栈中的位置为:
接下来,需要将 p 指向的 "hello world" 字符串地址获取出来。
include\acpi\platform\acenv.h
#ifndef va_arg
#ifndef _VALIST
#define _VALIST
typedef char *va_list;
#endif /* _VALIST */
/*
* Storage alignment properties
*/
#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)
/*
* Variable argument list macro definitions
*/
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
看代码最直观,args 为 char * 变量,
va_start() 获取传入函数的第一个变量的地址,即... 表示的变量,本例中,则是 p。对应 va_start 宏,先将保存fmt的地址取出(栈地址esp),然后对齐处理,取出p的值,根据该值既可以将所有的传入参数从栈中取出来。
最后调用 vprintk() 处理打印。
参考文档:http://www.eefocus.com/article/09-11/85907s.html