最新 x86_64 系統呼叫入口分析 (基于5.7.0)
整體概覽
最近的作業涉及系統呼叫入口,但網上的一些分析都比較老了,這里把自己的分析程序記錄一下,僅供參考,
x86_64位系統呼叫使用 SYSCALL 指令進入內核空間,使CPU切換到ring 0,SYSCALL 指令主要作業為從MSR暫存器加載CS/SS,以及系統呼叫入口(entry_SYSCALL_64),從而進入系統呼叫處理流程,
MSR暫存器相關這里不再介紹,需要相關知識的指路 暫存器總結 以及
Model-specific register,
SYSCALL 指令
IF (CS.L =? 1 ) or (IA32_EFER.LMA =? 1) or (IA32_EFER.SCE =? 1)
(* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *)
THEN #UD;
FI;
RCX ← RIP; (* Will contain address of next instruction *)
RIP ← IA32_LSTAR;
R11 ← RFLAGS;
RFLAGS ← RFLAGS AND NOT(IA32_FMASK);
CS.Selector ← IA32_STAR[47:32] AND FFFCH (* Operating system provides CS; RPL forced to 0 *)
(* Set rest of CS to a fixed value *)
CS.Base ← 0;
(* Flat segment *)
CS.Limit ← FFFFFH;
(* With 4-KByte granularity, implies a 4-GByte limit *)
CS.Type ← 11;
(* Execute/read code, accessed *)
CS.S ← 1;
CS.DPL ← 0;
CS.P ← 1;
CS.L ← 1;
(* Entry is to 64-bit mode *)
CS.D ← 0;
(* Required if CS.L = 1 *)
CS.G ← 1;
(* 4-KByte granularity *)
CPL ← 0;
SS.Selector ← IA32_STAR[47:32] + 8;
(* SS just above CS *)
(* Set rest of SS to a fixed value *)
SS.Base ← 0;
(* Flat segment *)
SS.Limit ← FFFFFH;
(* With 4-KByte granularity, implies a 4-GByte limit *)
SS.Type ← 3;
(* Read/write data, accessed *)
SS.S ← 1;
SS.DPL ← 0;
SS.P ← 1;
SS.B ← 1;
(* 32-bit stack segment *)
SS.G ← 1;
(* 4-KByte granularity *)
(代碼引自 https://www.felixcloutier.com/x86/syscall)
這里主要做了三個作業:
- 將RIP保存到RCX暫存器,即將SYSCALL指令下一條指令地址保存到RCX,后續用到,
- 從 IA32_LSTAR MSR 暫存器加載系統呼叫入口地址,64 位暫存器名為MSR_LSTAR,
- 從 IA32_STAR MSR 暫存器47-32到加載CS/SS段,64 位暫存器名為 MSR_STAR,其在內核啟動程序中初始化,
MSR暫存器初始化原始碼點這
核心為:
wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
入口地址
接下來就是進入 entry_SYSCALL_64處理流程,原始碼在這,
但是這里有一個問題:在較新版內核中,都已支持 PTI 機制,用戶態與內核態使用不同頁表,而這里 entry_SYSCALL_64 已經屬于內核代碼,而我們仔細觀察entry_SYSCALL_64 實作,在第四行才切換內核頁表,想要 entry_SYSCALL_64 能被執行,就需要 cpu_entry_area 的作用了,
SYM_CODE_START(entry_SYSCALL_64)
UNWIND_HINT_EMPTY
/* * Interrupts are off on entry. * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON, * it is too small to ever cause noticeable irq latency. */
swapgs
/* tss.sp2 is scratch space. */
movq %rsp, PER_CPU_VAR(cpu_tss_rw + TSS_sp2)
SWITCH_TO_KERNEL_CR3 scratch_reg=%rsp
cpu_entry_area 包括了CPU進入內核需要的所有資料/代碼,會被映射到用戶態頁表,了解點著,但是要注意較新版本cpu_entry_area已經不包含其中的 a set of trampolines;,至于為什么看這,
那又是怎么實作?
翻來覆去,終于在 pti 初始化處找到了關鍵點,其實作為
/* * Clone the populated PMDs of the entry and irqentry text and force it RO. */
static void pti_clone_entry_text(void){
pti_clone_pgtable((unsigned long) __entry_text_start,
(unsigned long) __irqentry_text_end,
PTI_CLONE_PMD);}
其將 __entry_text_start 開頭的地址復制,而這又與 entry_SYSCALL_64 有什么關系?我們繼續往下找
#define ENTRY_TEXT \
ALIGN_FUNCTION(); \
__entry_text_start = .; \
*(.entry.text) \
__entry_text_end = .;
而再看 entry_SYSCALL_64 定義的檔案頭部
.code64
.section .entry.text, "ax"
所以這里就會把 entry_SYSCALL_64 等一眾函式地址拷貝到用戶頁表,從而實作可訪問,具體定義展開這里就不進行了,
繼續執行
回到 entry_SYSCALL_64,我們跳過一系列處理,可以看到一個關鍵點:
call do_syscall_64
很顯然了,接下來就是執行 do_syscall_64 了,后面就是常規操作了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/465935.html
標籤:其他
上一篇:2022-Arch安裝(詳細)
下一篇:Set介面_network
