読者です 読者をやめる 読者になる 読者になる

OS自作入門 onLinux 1日目

OS自作を夢見る初心者が手にする本、「30日でできる!OS自作入門」です。



この本自体については賛否両論あるのですが、やはりこれほど単純にOSの作り方が
説明された本はないと思います。

反面C言語アセンブリ言語についての説明が不適当な箇所が
見られたりするということもあります。この本だけでOSが作れるかというと、
「ノー」と言わざるを得ません。

ですが、一度手に取ってほしい本ではあります。
この本だけにしかない魅力があります。



さて、この本最大のネックである「Windowsの開発」なのですが、
これを今回はLinuxで行ってみたいと思います。

同じようなことをやった他の方もいますし、ブログ記事も既に存在します。
実は俺自身が備忘録としてやっているだけなのは秘密
い、いや、きっとここでしか得られない情報があるはず!はず!

というわけで、拙速ながらもゆっくりとやっていきたいなーと思います。
==== バイナリエディタを使おう!
何はともあれ、まずはバイナリエディタの用意です。
Linuxで使えるバイナリエディタ

・hex
・hi
・bvi
・GHex

等々たくさんあるようですが、今回は俺の好み(?)でbviを採用します。

bviとは、viライクのバイナリエディタです。
Linuxをご存じの皆さんには、viの説明は要りませんね。

bvi.png

操作は基本的にviと同じです。ただし、書き込み操作の前には:set memmoveが必要です。
俺は一応ながらvimerの端くれです。たぶん。
:wq、:q!、a、i、r、xくらいしか使わないので、あとのことは知りません。

まずはbviをインストールします。当方の環境はLinux Mint 11です。

$ sudo apt-get install bvi

初期設定では、編集画面の横幅が16バイトになっておらず、
とても見づらいので設定します。

$ vi ~/.bvirc
(~/.bvircの内容)
set cm=16
set memmove

bviもvi同様、ホームフォルダの.bvircに書き込めば設定できます。
cmは一行に表示されるバイト数です。16にします。
さらに、.bvircでmemmoveをセットしておけば、編集時に入力する必要がなく、
非常に助かります。


○OSを作ろう!
続いて、自作本通り0x168000バイトを手で入力しましょう^^;

ss00.png


 完成図



b468bfa0.png


 ちゃんと最後まで入力したんですよ




と言いたいところですが、ハッキリ言って俺には0x168000バイトも手で入力する
根気もありませんし、たとえセロテープで「0」キーをはりつけたとしても、
入力が終わるまで待つ忍耐力はありません。

チートします。

20cff196.png

 
#include <stdio.h>
#include <ctype.h>

int main(int argc, char **argv) {
        FILE *fp;
        char null[1] = "\0";

        // open file
        if(!(fp = fopen(argv[1], "ab"))) {
                printf("Error to open file.\n");
                return 0;
        }

        long pos, max = 0xFF << 1;

        // get file size
        pos = ftell( fp );
        if( argc > 2 ) {
                max = atoi( argv[2] );
        }

        // write null data
        printf("Fill %ld to %ld\n", pos, max);
        int i;
        for( i = pos; i < max; i++ ) {
                fwrite( null, 1, 1, fp );
        }

        printf("Succeeded!\n");

        return 0;
}

これをコンパイルします。ソースの解説は省略。。。

$ gcc -o fat fat.c
$ ./fat helloos.img 1474560
Fill xxx to 1474560
Suceeded!

あ、ソースファイルの名前はfat.cにしておきました。こういうファイル肥大化ソフトは
fatって名前のことが多い気がします。
helloos.imgは、あのバイナリです。

0x001400辺りを過ぎるとすべて00になるので、そこまでは手入力して
あとはfatを使って残りを0x168000まですべて00で自動で埋めます。

ちなみに、1474560は何かというと、0x168000の10進数表記です。

$ python
>>> 0x168000
1474560

で調べました。python便利。
本来は、この程度はC言語じゃなくてスクリプト言語を使うべきなんでしょうが、
まともに使えるのがC言語くらいしかないので…(汗

さて、無事にバイナリイメージができたので、起動します。
そうか、QEMUの準備か。

QEMUを使おう!
とりあえずインストールしましょう。

$ sudo apt-get install qemu-kvm
$ qemu -fda helloos.img

40911e53.png

無事、起動できました。


○GASでアセンブルしてみよう!
まずは、nask用に書かれたソースをGAS用に直します。

.code16
.globl _start
_start:
.byte 0xeb, 0x4e, 0x90
.ascii "HELLOIPL"
.word 512
.byte 1
.word 1
.byte 2
.word 224
.word 2880
.byte 0xf0
.word 9
.word 18
.word 2
.int 0
.int 2880
.byte 0, 0, 0x29
.int 0xffffffff
.ascii "HELLO-OS   "
.ascii "FAT12   "
.skip 18, 0x00

.byte   0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
.byte   0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
.byte   0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
.byte   0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
.byte   0xee, 0xf4, 0xeb, 0xfd

.byte 0x0a, 0x0a
.ascii "hello, world"
.byte 0x0a
.byte 0

.org 0x1fe, 0x00

.byte 0x55, 0xaa

.byte 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
.skip 4600, 0x00
.byte 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
.skip 1469432, 0x00

naskとの対応は以下のようになります。
DB -> .byte
DW -> .word
DD -> .int
また、.asciiは文字列を置くときに使います。機能的には.byteと同じです。
.skipは.skip [バイト数], [データ]と使う事で、指定したデータで指定バイト数だけ埋めます。
そのほかの説明は省略します。

これをアセンブルします。アセンブラGCC付属のasを使います。
ところが、GCC自動的にELFフォーマットで出力するので、
これをバイナリ出力にするために、リンカスクリプトを書きます。

$ vi lnk.ls
(lnk.lsの内容)
OUTPUT_FORMAT("binary")

リンカスクリプトとは、GCCコンパイルするときに読み込むと、
バイナリレベルでいろいろと弄ることのできる設定ファイルみたいなものです。

OUTPUT_FORMAT("binary")で、ELFではなくバイナリをそのまま吐きます。

では、コンパイルしましょう。

$ gcc -nostdlib -o os.img temp.s -Tlnk.ls
-nostdlibオプションは、標準ライブラリをリンクしないという意味です。
今回は、temp.sという名前で保存したソースから、os.imgというバイナリを得ます。
-Tオプションでリンカスクリプトを指定します。


420467d5.png

このように、アセンブリに移行することができました。
これでアセンブリが使えるようになりました。

1日目はこれくらいで終了ー。

何か間違いやご要望等がありましたら、遠慮なくご指摘お願いします。