簡體   English   中英

內核如何獲取在linux下運行的可執行二進制文件?

[英]How does kernel get an executable binary file running under linux?

內核如何獲取在linux下運行的可執行二進制文件?

這似乎是一個簡單的問題,但誰能幫我深入挖掘? 如何將文件加載到內存以及如何開始執行代碼?

誰能幫我一步一步地告訴我發生了什么?

Linux 4.0 上exec系統調用的最佳時刻

找到所有這些的最好方法是使用 QEMU 對 GDB 內核進行單步調試: 如何使用 GDB 和 QEMU 調試 Linux 內核?

  • fs/exec.cSYSCALL_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定義。

      另請參閱: 為什么人們在 Python 腳本的第一行寫 #!/usr/bin/env python shebang?

    • 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的 PC 地址,同時還移動到較低特權的 CPU 狀態,例如 Ring 3 / EL0: 在操作上下文中 Ring 0 和 Ring 3 是什么系統?

    調度程序會被時鍾硬件定期喚醒,該硬件會按照內核之前的配置定期生成中斷,例如舊的 x86 PITARM 計時器 內核還注冊在定時器中斷被觸發時運行調度程序代碼的處理程序。

TODO:繼續進一步的源代碼分析。 我希望接下來會發生什么:

  • 內核解析 ELF 的/lib64/ld-linux-x86-64.so.2標頭以找到動態加載器(通常設置為/lib64/ld-linux-x86-64.so.2 )。
  • 如果存在:
    • 內核將動態加載器和要執行的 ELF 映射到內存
    • 動態加載器啟動,獲取指向內存中 ELF 的指針。
    • 現在在用戶空間中,加載程序以某種方式解析 elf 標頭,並對它們執行dlopen
    • dlopen使用可配置的搜索路徑來查找這些庫( ldd和朋友),將它們映射到內存,並以某種方式通知 ELF 在哪里可以找到其丟失的符號
    • loader 調用 ELF 的_start
  • 否則,內核會直接將可執行文件加載到內存中,而無需動態加載器。

    因此,它必須特別檢查可執行文件是否為 PIE 或是否將其放置在內存中的隨機位置: gcc 和 ld 中位置無關的可執行文件的 -fPIE 選項是什么?

來自linux 內核的兩個系統調用是相關的。 fork系統調用(或者可能是vforkclone )用於創建一個新進程,類似於調用進程(除了init之外的每個 Linux 用戶級進程都是由fork或朋友創建的)。 execve系統調用將進程地址空間替換為一個新的地址空間(本質上是通過對來自 ELF 可執行文件和匿名段的mmap段進行排序,然后初始化寄存器,包括堆棧指針)。 x86-64 ABI 補充Linux 程序集如何提供詳細信息。

動態鏈接發生在execve之后,涉及/lib/x86_64-linux-gnu/ld-2.13.so文件,對於 ELF 來說,它被視為“解釋器”。

閱讀已經引用的ELF 文檔后,您應該閱讀實際執行此操作的內核代碼

如果您無法理解該代碼,請構建一個UML Linux ,然后您可以在調試器中逐步執行該代碼。

您可以從了解可執行文件格式開始,例如 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM