要約
はりぼて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
命令のレファレンスによれば,相対ジャンプです.