三更燈火五更雞 |
而在《[code] 自己移動自己 - relocation》之中, 我獲得了 relocation 的技能, 現在結合 uefi os loader, 來打造一個 relocatable kernel。
也許你會問 relocatable kernel 和 os loader 會有關係嗎?
的確, relocation 的程式碼都在 kernel 上, 我所設計的方式並不是由 os loader 來做 relocation 的修改, kernel 一開始就會對自己做 relocation 的動作 (這段程式得保證是 relocation, 因為沒有人會來修正這段程式的 relocation), 不過由於之前 os loader 載入 kernel 的方式和這個不太一樣, 所以還是和 os loader 有點關係。
先提這次遇到的幾個問題:
在 debian/gcc 8.3 平台的環境編譯的 k64.elf, Dynamic section 的 RELASZ 是非 0 值, 這是正常的, 這個數字就是代表要修改幾個 relocation 數值; 但是在 Ubuntu 16.04.1 LTS, gcc (Ubuntu 8.1.0-5ubuntu1~16.04) 8.1.0/GNU ld (GNU Binutils for Ubuntu) 2.26.1 環境下編譯的 k64.elf, RELASZ 是 0, 所以程式不會做任何的 relocation 動作, 但是會多了一個 RELACOUNT, 紀錄 R_X86_64_RELATIVE 個數, 也可以拿來計算要做幾次 relocation 的修改。
Program Headers 在某個 linker script 會產生 2 個 LOAD 標籤的 segment, 有時候只有一個, 我是想要有 2 個, 將 text, rodata section 集合成一個 segment, 另一個是 data section 集合的 segment, 不過目前還不知道怎麼寫出這種 linker script。
先把上述 2 個問題記錄下來, 也許以後有機會可以解除疑惑。
[載入 elf64 kernel]
首先要載入 elf64 kernel, 直接載入一個 elf64 kernel, 比較麻煩的是要算出那個位址才是 kernel 的進入點。和載入固定位址的 kernel 不同, 我打算算出這個 kernel entry, 直接跳到該位址。
這要從 elf program header 來觀察, 需要 segment 的資訊。list 0 L14, L16 是這個 elf 的可執行的相關內容, 要載入一個 elf 執行檔, 就是把這部份載入到對應的記憶體位址, 在跳到 entry 就可以執行這個 elf, 寫一個 elf loader 比想像中還容易吧!
以下的欄位就是載入/執行 elf 的關鍵。
offset | virtAddr | filesiz |
---|---|---|
0 | 4000 | 299d |
3000 | 7000 | 442c |
這個 elf 的 entry point 是 5010。
只要參照 list 1 的 pseudo code, 就可以寫出載入/執行 elf 的程式, 也就是 elf loader。
這樣就跳到這個 elf 執行檔並且開始執行。
fig 1 對應到 list 0 的 LOAD segment, 目標要算出 entry point X 是多少?
fig 1 左邊的圖 0, 3000 是 elf file 的 offset, 右邊的圖是在記憶體的絕對位址。
fig 1 左邊的圖 0x1000 是我假設的載入到記憶體的位址。
假設 elf 被載入到 0x1000 的位址,
先用
entry 5010
offset: 0
virtAddr: 4000
來計算:
X - 0 = 5010 - 4000
X = 1010
第二組
entry 5010
offset: 3000
virtAddr: 7000
3000 - X = 7000 - 5010
X = 1010
不管用那一組 segment 值做計算, 都會得到 1010, 所以不用擔心要用那一組 segment 的值來計算。
X 就是在 elf 開始的 offset, 加上 elf 的載入位址 0x1000 就是要跳去執行的 entry point, 也就是 1010 + 1000 = 2010。
fig 1 elf load segment layout |
所以在載入這個 elf 到 0x1000 位址後, 跳到 2010 的位址執行就可以了。
如果這個 elf 是載入到 0x6000 位址後, 則跳到 7010 的位址執行。
而 elf 是透過 uefi boot service api 載入的。
這樣就不用一定要把 segment 複製到 0x3000, 0x7000 在跳去 0x5010, 當然, 前提是這個 elf 執行檔, 可以在任意位址執行才行。
下篇讓我們看看怎麼讓 elf 執行檔有這個能力。
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。