はりぼてOSのカーネルへのジャンプについて
要約
はりぼてOSでは,まず絶対ジャンプでカーネルのヘッダに飛んだ後,そこから相対ジャンプでHariMainに飛ぶという,二段階ジャンプを行っていることが判りました.
はじめに
はりぼてOSにページングを実装する事に奮闘していました.JMP 0xC0000000でカーネルに飛んでも何も表示されなかったため,調べてみると,はりぼてOSはまずカーネルの先頭にジャンプした後,そこにあるJMP命令*1で更にジャンプしていることがわかりました.このジャンプ命令について調べてみました.
注意
筆者はtolsetを使用せず,リンカはGNU ldを使用しています.
解説
JMP DWORD 2*8:0x0000001bの到着地点
asmhead.nasの最後でこの命令を実行します.これは保護モードで実行されているため,2番目のセグメントの0x1b番地に飛びます.2番目のセグメントの先頭はhrbファイルの先頭に等しいため,このジャンプによってhrbファイルの先頭から0x1b離れた位置に飛びます.はりぼてOSのasmhead.nasで仮に設定しているGDTに関してはこちらを確認してください.
hrbファイルの0x1b番地には0xE9があります.本のP.461にも書いてありますが,これはJMP命令の機械語です.
到着地点にあるJMP命令の行き先
本のP.461によれば,JMP命令で飛ぶ行き先は,アプリの開始地点 - 0x20*2となっています.bim2hrbがどのような形式になっているか分からないため,ここではGNU ldを使用した場合について解説します.ヘッダが以下のように指定されているとします.
.head 0x0 : { LONG(64 * 1024) LONG(0x69726148) LONG(0) LONG(0x310000) LONG(SIZEOF(.data)) LONG(LOADADDR(.data)) LONG(0xE9000000) LONG(HariMain - 0x20) LONG(0) }
リンカスクリプト内では,アプリの開始地点 - 0x20をHariMain - 0x20と指定します.この時,アドレスHariMainは,.headセグメントの先頭を基点とする相対アドレスとなります.0x20は.headセグメントの最初からLONG(HariMain - 0x20)までが使用しているメモリの大きさに等しいです*3.この0x20がない場合,ここにあるJMP命令はHariMainよりも更に0x20だけ先にあるメモリ番地にジャンプすることになり,カーネルが正しく動作しなくなります.0x20を引くことで,カーネルの先頭に正しく飛ぶことが出来ます.
また,このJMP命令は,Intel® 64 and IA-32 Architectures Software Developer Manuals | Intel® SoftwareのVolume 2, Chapter 3-2のJMP命令のレファレンスによれば,相対ジャンプです.