2019年7月18日 星期四

uefi os loader (5.5) - 載入 relocatable kernel (2) - 實作篇 - image_base

努力という種をまけば、幸せという花が咲く
看完「原理篇」之後, 這篇要說明怎麼實作。

步驟有以下這些:
  1. 找出 image_base
  2. 找到 relocation 的資訊
[找出 image_base]

為了減少複雜度, 本篇只有討論 image_base。

image_base 主要用來輔助, 可以用它來計算出一些 relocation 相關的操作。它是在 k.ld L5 中描述的, 並不存在於程式碼中, 這是一個很特殊的技巧, 一般的程式並不會這樣寫。

怎麼找出它的值呢? 找出執行的 image_base 比想像中還容易, 不過這部份的程式碼和平台有關, 沒有一個通用的作法, 至少 x86_64 和 x86_32 作法是不一樣的。

以下說明 x86_64 的程式碼實作:

k_crt.S L14 就是在取得執行時期的 image_base, 這是 x86_64 的 lea 用法, 它會把 image_base 執行時期的位址存到 %rdi。

x86 32bit 的 lea 沒有這種用法, 並不是改成 edi 就會有一樣的行為, x86 32bit 是用不同的作法來達成類似的事情。你可能覺得很神奇, 這是怎麼辦到了, 不過我實在不想解釋這個, 請參考: 从机器码理解RIP 相对寻址

參考 fig 1 右圖, 如果當 elf 執行檔被載入到 0x6882018 時, image_base 是 0x687e018; fig 1 左圖則是按照 elf 的 load segment 載入到記憶體的樣子。

fig 1 左圖:
elf_entry_point - image_base = 0x5010 - 0x0 = 0x5010

fig 1 右圖:
new_elf_entry_point - new_image_base = 0x6883028 - 0x687e018 = 0x5010

elf_entry_point 和 image_base 的差是不會變的。

k.ld
 1 ENTRY(_start)
 2 
 3 SECTIONS
 4 {
 5   image_base = .;
 6   . = 0x5000;
 7   .hash : { *(.hash) }
 8   .text :
 9   {
10     *(.text)
11   }
12   . = ALIGN(4096);
13   .reloc : {
14    *(.reloc)
15   }
16   .= ALIGN(32);
17   .rodata :
18   {
19     *(.rodata)
20   }
21   .= ALIGN(32);
22 
23   .data :
24   {
25     *(.data)
26   }
27 
29   .= ALIGN(32);
30   __bss_start__ =.;
31   .bss :
32   {
33     *(.bss)
34   }
35   __bss_end__ = .;
36 
...
61 }

fig 1 load kernel elf to 0x6882018 address

k_crt.S
 1         .text
 2         .align 4
 3
 4         .globl _start
 5 _start:
 6         subq $8, %rsp
 7
 8         pushq %rcx
 9         pushq %rdx
13
14         lea image_base(%rip), %rdi
15         lea _DYNAMIC(%rip), %rsi
16
17         call k_relocate
18
19         call k_main

呼! 是不是短很多, 看起來應該輕鬆些了。

好了, 有了 image_base, 我們就可以發大財了! 厄 ... 是拿來處理 relocation。

下一篇會有點複雜, 複雜到我都不想寫了!

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。