備忘録やめた

備忘録として使用していたけどやめた.このブログに載せてあるコードのライセンスは別途記載がない限りWTFPL OR NYSLです.

はりぼて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に関してはこちらを確認してください.

tokuchan3515.hatenablog.com

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)
    }

リンカスクリプト内では,アプリの開始地点 - 0x20HariMain - 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命令のレファレンスによれば,相対ジャンプです.

つまるところ,はりぼてOSは二段階右折ならぬ二段階ジャンプによって,カーネル本体にジャンプしていることになります.

*1:機械語だと0xE9

*2:この場合はカーネルの開始地点 - 0x20

*3:LONGが4バイトを使用しているため