2.6.18内核head.S中.asciz字符串%p格式符解析疑问
Linux 2.6.18 i386内核中
int_msg字符串%p的输出逻辑 问题背景
在2.6.18版本的i386内核源码中,arch/i386/kernel/head.S里定义了如下字符串:
int_msg: .asciz "Unknown interrupt or fault at EIP %p %p %p\n"
该字符串由ignore_int代码段调用:
ignore_int: cld #ifdef CONFIG_PRINTK pushl %eax pushl %ecx pushl %edx pushl %es pushl %ds movl $(__KERNEL_DS),%eax movl %eax,%ds movl %eax,%es pushl 16(%esp) pushl 24(%esp) pushl 32(%esp) pushl 40(%esp) pushl $int_msg #ifdef CONFIG_EARLY_PRINTK call early_printk #else call printk #endif addl $(5*4),%esp popl %ds popl %es popl %edx popl %ecx popl %eax #endif iret
疑问:无文档说明%p的用法,汇编器或运行时如何确定%p %p %p的输出内容?
解答
1. %p的本质:内核打印函数的格式占位符
%p不是汇编器的语法,它是Linux内核printk(或early_printk)的格式占位符,由运行时的打印函数解析处理,专门用于输出指针/内存地址。在i386架构下,它会把传入的32位整数以十六进制形式输出(早期内核版本可能不带0x前缀,但核心是输出地址值)。
2. 三个%p对应的参数来源
x86架构采用从右往左的栈传参规则,调用printk前的压栈顺序决定了参数的传递:
- 先压入四个栈偏移参数(
16(%esp)、24(%esp)、32(%esp)、40(%esp)) - 再压入格式字符串
int_msg
因此printk的实际调用逻辑等价于:
printk(int_msg, arg1, arg2, arg3, arg4);
由于格式字符串只有三个%p,最后一个参数arg4会被忽略。
结合中断栈帧的结构(中断发生时CPU自动压入EFLAGS、CS、EIP,再加上函数内手动保存的寄存器),三个有效参数对应:
- 第一个
%p:中断触发时的CS寄存器值(代码段选择子) - 第二个
%p:中断触发时的EIP寄存器值(就是字符串里标注的出错指令地址) - 第三个
%p:中断触发时的EFLAGS寄存器值(CPU标志寄存器的状态)
3. 偏移量的意义
代码中用16(%esp)这类偏移量,是因为函数开头已经手动压入了%eax、%ecx、%edx、%es、%ds五个寄存器,需要通过偏移跳过这些手动保存的内容,直接访问CPU在中断发生时自动保存的上下文数据。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

