x86 Page Fault 处理
Published:
x86 中异常处理源代码定义复杂,此日志用于记录源代码分析流程,基于内核版本 5.19.0。
通常 Linux 中出现异常时处理流程如下。
asm_exc_page_fault()汇编声明的异常处理入口 @arch/x86/include/asm/idtentry.hexc_page_fault()C 语言版本,由asm_exc_page_fault()调用 @arch/x86/mm/fault.cdo_kernel_page_fault()异常来自于内核时,进行处理do_user_page_fault()异常来自于用户时,进行处理
asm_exc_page_fault() 声明
该函数声明比较复杂,由 DECLARE_IDTENTRY_RAW_ERRORCODE 宏展开而成。
该宏分成两部分,一部分由 C 语言编译器处理,另一部分则由汇编器处理。
C 语言部分
#define DECLARE_IDTENTRY_ERRORCODE(vector, func) \
asmlinkage void asm_##func(void); \
asmlinkage void xen_asm_##func(void); \
__visible void func(struct pt_regs *regs, unsigned long error_code)
/* ... */
#define DECLARE_IDTENTRY_RAW_ERRORCODE(vector, func) \
DECLARE_IDTENTRY_ERRORCODE(vector, func)
汇编部分
#define DECLARE_IDTENTRY(vector, func) \
idtentry vector asm_##func func has_error_code=0
#define DECLARE_IDTENTRY_ERRORCODE(vector, func) \
idtentry vector asm_##func func has_error_code=1
其中汇编部分调用了 idtentry 汇编宏进行二次展开,其定义位于 arch/x86/entry/entry_64.S
/**
* idtentry - Macro to generate entry stubs for simple IDT entries
* @vector: Vector number
* @asmsym: ASM symbol for the entry point
* @cfunc: C function to be called
* @has_error_code: Hardware pushed error code on stack
*
* The macro emits code to set up the kernel context for straight forward
* and simple IDT entries. No IST stack, no paranoid entry checks.
*/
.macro idtentry vector asmsym cfunc has_error_code:req
SYM_CODE_START(\asmsym)
UNWIND_HINT_IRET_REGS offset=\has_error_code*8
ENDBR
ASM_CLAC
cld
.if \has_error_code == 0
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif
.if \vector == X86_TRAP_BP
/*
* If coming from kernel space, create a 6-word gap to allow the
* int3 handler to emulate a call instruction.
*/
testb $3, CS-ORIG_RAX(%rsp)
jnz .Lfrom_usermode_no_gap_\@
.rept 6
pushq 5*8(%rsp)
.endr
UNWIND_HINT_IRET_REGS offset=8
.Lfrom_usermode_no_gap_\@:
.endif
idtentry_body \cfunc \has_error_code
_ASM_NOKPROBE(\asmsym)
SYM_CODE_END(\asmsym)
.endm
接下来对这段代码进行分析,前四行代码都是对特定特性进行处理。
UNWIND_HINT_IRET_REGS提供栈回溯信息,伪指令,不会被编译进实际二进制。ENDBRIntel x86 CET 扩展,指明此处可以被jmp抵达。ASM_CLACIntel x86 SMAP 扩展,允许内核访问用户空间地址。cldx86 方向寄存器,清空确保循环递增方向一致。
接下来的 .if 伪指令判断是否为断点,如果是来自内核的断点,对栈进行特殊处理。 最后进入 idtentry_body。
