RamenOSのカーネルの.headセグメントを削除する

- 作者:川合 秀実
- 発売日: 2006/03/01
- メディア: 単行本
要約
RamenOSのカーネルの.headセグメントを削除します..headセグメントに依存している,asmhead.nas内の; bootpackの起動というコメントで始まる.dataセグメント移動のコードを削除し,リンカスクリプトを編集して最初から.dataセグメントを定位置に置きます.そして.textセグメントの先頭に,最初に実行されるべき関数を配置します.この作業を行なうことで,カーネルの.headセグメントを削除することが出来ます.
.headセグメントを削除したら,asmhead.nasの最後のJMP命令をJMP 2*8:0に変更する必要があります.
はじめに
はりぼてOSを基にしているRamenOSにページングを実装するため,はりぼてOSのhrbファイルの最初についているデータを削除する必要がありました.カーネルの機械語コードがファイルの先頭に位置して欲しかったためです.この記事ではhrbファイルの先頭についているデータすなわち.headセグメントを削除する方法を紹介します.
注意
現時点で筆者は8日目の内容を終了した段階にあります.従ってはりぼてOSのアプリケーションを製作していません.本中のP460,P461によれば,一部のデータはアプリケーションを実行する際に必要とされています.
筆者はbim2hrbを使用せず,GNU ldを使用しています.この記事では,GNU ldを使用した方法のみを紹介します.
やり方
.headセグメント削除前のリンカスクリプトは以下の通りです.
OUTPUT_FORMAT(binary); OUTPUT_ARCH(i386); ENTRY(os_main) SECTIONS { .head 0 : { LONG(64 * 1024) LONG(0x69726148) LONG(0) LONG(ADDR(.data)) LONG(SIZEOF(.data)) LONG(LOADADDR(.data)) LONG(0xE9000000) LONG(os_main - 0x20) LONG(0) } .text : { *(.text*) } .data 0x310000 : AT ( ADDR(.text) + SIZEOF(.text) ) { *(.data) *(.rodata*) *(.bss) } /DISCARD/ : { *(.eh_frame) } }
このスクリプトの.headセグメントを削除します.
現時点で不要な情報を削除する
LONG(64 * 1024),LONG(0x69726148),2つのLONG(0)は,8日目の内容を完成させた時点では必要ありません.その後必要になるかもしれませんが,現状残しておく必要がないため削除します.
データセグメントを初めから定位置に置く
リンカスクリプトによれば,.dataセグメントのアドレス解決をする際,.dataセグメントが0x310000から始まると仮定していますが,実際は.textセグメントの直後に配置するようにしています.しかし,asmhead.nas内で.dataセグメントは0x310000に移されることになります.
; asmheadでしなければいけないことは全部し終わったので、 ; あとはbootpackに任せる ; bootpackの起動 MOV EBX,BOTPAK MOV ECX,[EBX+16] ADD ECX,3 ; ECX += 3; SHR ECX,2 ; ECX /= 4; JZ skip ; 転送するべきものがない MOV ESI,[EBX+20] ; 転送元 ADD ESI,EBX MOV EDI,[EBX+12] ; 転送先 CALL memcpy skip:
[EBX+16],[EBX+20],[EBX+12]はどれも.headセグメントの情報を参照しています.それぞれLONG(SIZEOF(.data)),LONG(LOADADDR(.data)),LONG(ADDR(.data))です.
このコードは,.dataセグメントが存在するかを確認し,存在する場合,.dataセグメントを,.dataセグメントが実際に存在する場所から,アドレス解決の際の.dataセグメントの起点アドレスへ移します.
この移す作業を取り除き,最初から定位置に移動させます.ただし,今回は.textセグメントの直後に置きます.
このためには,先程のアセンブリコードを削除し,リンカスクリプトを次のように変更します.
.text 0x00501000 : { *(.text*) } .data : { *(.data) *(.rodata*) *(.bss) }
0x00501000という値はRamenOSにおいてカーネルが置かれる位置です.はりぼてOSの場合ここを0x00280000に変更してください.詳しくはこちらで.
これらの変更によって,LONG(ADDR(.data)),LONG(SIZEOF(.data)),LONG(LOADADDR(.data))の3つを削除することが出来ます.
ところで,はりぼてOSではスタック領域を0x00300000から0x003FFFFFとしています.つまり,.textセグメントと.dataの間の領域がスタック領域となっています.RamenOSでは別の場所をスタック領域としますが,この段階では仮にカーネルの始点をスタックの基点として用意します.従って以下のようにスタックポインタの初期値を変更します.
MOV ESP,BOTPAK ; スタック初期値
*(.text.os_main)を.textセグメントに追加
カーネルのコードで最初に実行されるべき関数を先頭に配置する必要があります.RamenOSではos_main関数を最初に実行する必要があるので,この関数を.textセグメントの先頭に配置します.
.text 0x00501000 : { *(.text.os_main) *(.text*) }
*(.text.os_main)の最初のアスタリスクは,ld実行時,全ての入力ファイルから受け付けるという意味です.例えば,ld a.o b.o c.oを実行した時に,3つのオブジェクトファイルのどれに.text.os_mainが存在しても,それを配置します.特定のファイルからの.text.os_mainのみには制限しません.
JMP命令の削除
最後に残った2つのLONGについては以下の記事を確認してください.
この時点で,.headセグメントを削除する事が出来ます.残った2つのLONGはOSの他のコードが依存していないためです.
以上の作業により,はりぼてOSのカーネルファイルの.headセグメントを削除することが出来ます.尚,.headセグメントを削除したため,asmhead.nasの最後のJMP命令を以下のように変更する必要があります.
JMP DWORD 2*8:0