- 論壇徽章:
- 3
|
本帖最后由 captivated 于 2020-03-31 18:44 編輯
x86 arch 的啟動代碼比較復雜,為了說清楚我的具體問題,所以先做一個簡述(免得說我自己沒把基本問題搞清楚,省點抬杠時間)。
此外以下簡述主要針對 x86_64 arch 而不是 i386.
1. 啟動映像生成:
x86 arch 的啟動映像為 $OUT/arch/x86/boot/bzImage. $OUT 表示 kernel 編譯輸出目錄(如果編譯時不指定 -o 輸出目錄,則和編譯源碼是同一個目錄)。
bzImage 構成如下。為了方便,$OUT/arch/x86/boot 目錄簡稱為 $(boot) 路徑.
1) 最終的 bzImage 是由 $(boot)/setup.bin 和 $(boot)/vmlinux.bin 組裝成的.
$(boot)/vmlinux.bin 又由 $(boot)/compressed/vmlinux 通過 objcopy 剝離掉 .comment 和 .note 段而成.
而 $(boot)/compressed/vmlinux 則由 $(boot)/compressed 下面的各個目標文件組成, 里面最重要的是
$(boot)/compressed/piggy.o 這個文件.
- bzImage <-+ $(boot)/setup.bin
- #######-+ $(boot)/vmlinux.bin <-+ $(boot)/compressed/vmlinux <-+ $(boot)/compressed/head_64.o
- ############################################### $(boot)/compressed/misc.o
- ############################################### ...
- ############################################### $(boot)/compressed/piggy.o
復制代碼
2) 為什么說 piggy.o 重要, 因為它是由壓縮了的 vmlinux 構成的. 這里默認選 gzip 壓縮.
而 vmlinux.bin.gz 又是由 compressed 同目錄下的 vmlinux.bin 和 vmlinux.relocs 構成的.
vmlinux.bin 和 vmlinux.relocs, 則來自于 $OUT 目錄下的 VMLINUX(實際文件名就是 $OUT/vmlinux. 大寫的意思是表示它其實就是編譯最終的鏈接文件, 從它到最終的 bzImage 不過是一堆二進制工具為了啟動加載協(xié)議做的一堆變換而已).
- $(boot)/compressed/piggy.o <+ $(boot)/compressed/vmlinux.bin.gz <-+ $(boot)/compressed/vmlinux.bin
- ############################################### $(boot)/compressed/vmlinux.relocs
復制代碼
2. x86 arch 啟動問題
x86 相關的 boot protocal 有幾種. 不管哪一種,總之 bzImage 得要 bootloader 加載到內存里面然后去運行才行.
前面的映像構建過程為什么是那樣子, 估計也和 x86 arch “歷史悠久”, boot protocal 復雜相關.
但大概來說, 有這么幾種 boot protocal:
1) 實模式 boot protocal.
這種情況下, bootloader 是運行在實模式的. 就算 bootloader 自己切換了保護模式, 它加載完 kernel(bzImage)并跳轉過去執(zhí)行前, 也得切回實模式, 因為 protocal 就是協(xié)議嘛, 大家要有個商量. 這時 bootloader 切換過去后, 最先從 setup.bin 開始運行(bzImage 是由 setup.bin + vmlinux.bin 包的), 也就是走
arch/x86/boot/header.S -> _start -> calll main
arch/x86/boot/main.c -> main -> go_to_protected_mode
arch/x86/boot/pm.c -> go_to_protected_mode -> protected_mode_jump
arch/x86/boot/pmjump.S -> GLOBAL(protected_mode_jump) -> jmpl *%eax
經(jīng)過這些步驟, 就會開始執(zhí)行到 vmlinux.bin 里面的東西了.
2) 32 位 boot protocal
現(xiàn)在 bootloader 已經(jīng)很強大了, 所以通常 bootloader 自己已經(jīng)切到 32 位 protected mode, 然后就不走 setup.bin 那一套了.
前面說了 bzImage 是由 setup.bin + vmlinux.bin(都在 $(boot) 目錄下)組裝而成, 跳過 setup.bin 的話, 當然就直接從 vmlinux.bin 開始執(zhí)行了.
bootloader 為什么可以這樣做, 因為 bzImage 就是 bootloader 加載的, 它當然知道 setup.bin vmlinux.bin 分別在什么內存位置.
一般來說, setup.bin 必須要運行在實模式, 因此其加載地址是物理內存 1MB 以下的, 而 vmlinux.bin 會剛剛好從 1MB 位置開始放置.
anyway, 這時候走的是這個:
arch/x86/boot/compressed/head_64.S -> ENTRY(startup_32) -> ENTRY(startup_64) -> jmp *%eax
這里還要說明一下. 前面映像構建過程說得清楚, 真正的 kernel 是 piggy.o(由 $(OUT)/vmlinux 壓縮, 然后生成的目標文件).
startup_32 -> startup_64 主要是從 32 位保護模式切換到 64 bit long mode. 切換之前會建立一個 4GB 簡單 identity map(因為切換到 64 bit long mode 的條件之一就是, paging 必須是開的, 即 CR0.PG == 1).
而 startup_64 呢, 則會調用一個解壓縮函數(shù)將 vmlinux.bin.gz 解壓, 然后跳轉到解壓后的 kernel 入口去執(zhí)行. 這里面可能還會有重定位處理等等.
如果關掉 KASLR, 那么解壓后的 kernel, 也就是真正的 $(OUT)/vmlinux, 默認會放在 16MB 的位置.
3) 64 位 boot protocal
顧名思義, 就是 bootloader 有點過分強大(或者討厭)了, 跳轉到 kernel 之前, 它會自己先切到 64 bit long mode.
也就是直接執(zhí)行 arch/x86/boot/compressed/head_64.S 中的 startup_64 了.
這時最初的 paging 是 bootloader 建立的.
3. vmlinux entry
好了,前面這些復雜的過程略過. 不管是先從 arch/x86/boot/setup.S 開始, 還是先從 arch/x86/boot/compressed/head_64.S 開始,
最終解壓縮后的 kernel 入口點是:
arch/x86/kernel/head_64.S 里面的 startup_64 函數(shù)(or 一個全局符號).
有人可能會問這不和 compressed/head_64.S 里面的 startup_64 重名嗎, 答案是它們根本沒鏈接在一起, 各管各的, 不會有重定義錯誤放心.
啊, 約了不知道多少次電影請喝了多少次奶茶爬了多少次山, 終于可以約爬床了, 終于寬衣解帶了, 興奮之情溢于言表啊...
結果才看了特么幾行, 我就被郁悶到了...
...
具體問題敘述還有點小麻煩, 呆會貼上來.
...
|
|