- 作者:川合 秀実
- 発売日: 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