[英]How does kernel get an executable binary file running under linux?
內核如何獲取在linux下運行的可執行二進制文件?
這似乎是一個簡單的問題,但誰能幫我深入挖掘? 如何將文件加載到內存以及如何開始執行代碼?
誰能幫我一步一步地告訴我發生了什么?
Linux 4.0 上exec
系統調用的最佳時刻
找到所有這些的最好方法是使用 QEMU 對 GDB 內核進行單步調試: 如何使用 GDB 和 QEMU 調試 Linux 內核?
fs/exec.c
在SYSCALL_DEFINE3(execve
只需轉發到do_execve
。
do_execve
轉發到do_execveat_common
。
do_execveat_common
要查找下一個主要函數,請跟蹤上次修改返回值retval
的時間。
開始構建struct linux_binprm *bprm
來描述程序,並將其傳遞給exec_binprm
執行。
exec_binprm
再次按照返回值查找下一個主要調用。
search_binary_handler
處理程序由可執行文件的第一個魔術字節確定。
兩個最常見的處理程序是用於解釋文件 ( #!
magic) 和用於 ELF ( \\x7fELF
magic) 的處理程序,但還有其他內置於內核中的處理程序,例如a.out
。 用戶也可以通過/proc/sys/fs/binfmt_misc注冊自己的
ELF 處理程序在fs/binfmt_elf.c
定義。
formats
列表包含所有處理程序。
每個處理程序文件包含如下內容:
static int __init init_elf_binfmt(void) { register_binfmt(&elf_format); return 0; }
elf_format
是在該文件中定義的struct linux_binfmt
。
__init
是一種魔法,它將代碼放入內核啟動時調用的魔法部分: __init 在 Linux 內核代碼中意味着什么?
鏈接器級依賴注入!
還有一個遞歸計數器,以防解釋器無限執行。
試試這個:
echo '#!/tmp/a' > /tmp/a chmod +x /tmp/a /tmp/a
我們再次跟蹤返回值,看看接下來會發生什么,看看它來自:
retval = fmt->load_binary(bprm);
其中load_binary
是為結構上的每個處理程序定義的:C 風格的多態性。
fs/binfmt_elf.c:load_binary
是否實際工作:
struct pt_regs
初始寄存器設置start_thread
,它將進程標記為可供調度程序調度最終調度程序決定運行該進程,然后它必須跳轉到存儲在struct pt_regs
的 PC 地址,同時還移動到較低特權的 CPU 狀態,例如 Ring 3 / EL0: 在操作上下文中 Ring 0 和 Ring 3 是什么系統?
調度程序會被時鍾硬件定期喚醒,該硬件會按照內核之前的配置定期生成中斷,例如舊的 x86 PIT或ARM 計時器。 內核還注冊在定時器中斷被觸發時運行調度程序代碼的處理程序。
TODO:繼續進一步的源代碼分析。 我希望接下來會發生什么:
/lib64/ld-linux-x86-64.so.2
標頭以找到動態加載器(通常設置為/lib64/ld-linux-x86-64.so.2
)。dlopen
dlopen
使用可配置的搜索路徑來查找這些庫( ldd
和朋友),將它們映射到內存,並以某種方式通知 ELF 在哪里可以找到其丟失的符號_start
否則,內核會直接將可執行文件加載到內存中,而無需動態加載器。
因此,它必須特別檢查可執行文件是否為 PIE 或是否將其放置在內存中的隨機位置: gcc 和 ld 中位置無關的可執行文件的 -fPIE 選項是什么?
來自linux 內核的兩個系統調用是相關的。 fork系統調用(或者可能是vfork
或clone
)用於創建一個新進程,類似於調用進程(除了init
之外的每個 Linux 用戶級進程都是由fork
或朋友創建的)。 execve系統調用將進程地址空間替換為一個新的地址空間(本質上是通過對來自 ELF 可執行文件和匿名段的mmap段進行排序,然后初始化寄存器,包括堆棧指針)。 x86-64 ABI 補充和Linux 程序集如何提供詳細信息。
動態鏈接發生在execve
之后,涉及/lib/x86_64-linux-gnu/ld-2.13.so
文件,對於 ELF 來說,它被視為“解釋器”。
您可以從了解可執行文件格式開始,例如 ELF。 http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
ELF 文件包含幾個帶有標題的部分,這些部分描述了二進制文件的各個部分應如何以及在何處加載到內存中。
然后,我建議閱讀加載二進制文件和處理動態鏈接的 linux 部分, ld-linux 。 這也是對ld-linux的一個很好的描述: http : //www.cs.virginia.edu/~dww4s/articles/ld_linux.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.