x86 Page Fault 处理
x86 中异常处理源代码定义复杂,此日志用于记录源代码分析流程,基于内核版本 5.19.0
。
通常 Linux 中出现异常时处理流程如下。
asm_exc_page_fault()
汇编声明的异常处理入口 @arch/x86/include/asm/idtentry.h
exc_page_fault()
C 语言版本,由asm_exc_page_fault()
调用 @arch/x86/mm/fault.c
do_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
提供栈回溯信息,伪指令,不会被编译进实际二进制。ENDBR
Intel x86 CET 扩展,指明此处可以被jmp
抵达。ASM_CLAC
Intel x86 SMAP 扩展,允许内核访问用户空间地址。cld
x86 方向寄存器,清空确保循环递增方向一致。
接下来的 .if
伪指令判断是否为断点,如果是来自内核的断点,对栈进行特殊处理。
最后进入 idtentry_body
。