【OS自作】littleosbook をやってみる #1
べつに暇なわけじゃないけど、たまには低レイヤなことをしたい。簡易的なオペレーティング・システムの開発方法を解説したサイト The little book about OS development を見つけたのでやってみる。ページは以下。
追記:本家の littleosbook は不備が多いがメンテナンスされてない。以下はフォークされたリポジトリであり、かなりの不備が修正されている。絶対こっち見るほうがオススメ。
作業内容をここにメモって残しておけば、この先10年で3人くらいの役には立つだろう。このチュートリアルはけっこう長いので、ブログ記事としては何回かに分けることになりそう。
簡易的なコンパイラはいくつか作ったことあるけど OS は作ったことない。調べながらやればなんとかなるだろう。ちなみに使用した環境は Ubuntu の 20.04.3。
1 Introduction
導入部分。想定読者のレベルはそこそこ高そう。低レイヤなプログラミングには慣れていないが、まぁなんとかなるだろう。
2 First Steps
さっそくプリミティブな OS を作るところから始まる。
2.1 Tools
環境構築とブートに仕組みについて。x86 用の仮想マシンとして Bochs を使うらしい。
2.2 Booting
BIOS が起動してブートローダが起動する。そしてブートローダが OS のプログラムをロードすることで OS を起動する。ブートローダってちゃんと調べたことないから後で Wikipedia でも読もう。
2.3 Hello Cafebabe
最小の OS を実装する。手順通りに進めてゆく。まずはアセンブリを書いてコンパイルし object file を生成する。そして、実行可能ファイルを生成するために linker script を書いて ld コマンドを実行する。その結果 kernel.elf というファイルが生成される。これが OS カーネルの実行可能ファイルである。
次に、ブートローダである GRUB を入手する。記載されている GitHub 上の URL が誤っている。正しくは ここ にある。OS の実行可能ファイルとブートローダを格納して ISO イメージを作成する。その結果 os.iso というファイルが作成される。
Bochs を使って作成した OS をエミュレータで実行する。ここで実行が上手くいかなかった。どうやら、実行画面を描画するための display library として指定している sdl のインストールが上手くいっていない。準備段階で実行した apt-get install bochs-sdl
が上手く機能しないらしい。この問題は以下の issue でも報告されていた。
issue にあるように、この問題は display library として sdl の代わりに X Window System を使うようにすると解決した。具体的には sudo apt-get install bochs-x
を実行した後、以下のように Bocks の設定として x
を指定した。
megs: 32 display_library: x (省略) cpu: count=1, ips=1000000
そして、コマンド bochs -f bochsrc.txt -q
を叩くとデバッガが起動する。プロンプトで c
コマンドを実行するとエミュレータ画面に何かしらの遷移がある。画面に何が表示されているのかはよく分からない。q
コマンドでエミュレータを終了する。実行ログを見ると cafebabe
という値が eax レジスタに格納されているようなので上手くいってそう。
$ grep cafebabe bochslog.txt 02120339208i[CPU0 ] | EAX=cafebabe EBX=0002cd80 ECX=00000001 EDX=00000000
3 Getting to C
OS のコードを書くためにアセンブリではなく C 言語を使うための準備をする。
3.1 Setting Up a Stack
C プログラムを実行するためにはスタック領域が必要である。つまり、カーネルプログラムがメモリにロードされた後、カーネルがスタック領域を使って様々な OS の仕事をできるようにする必要がある。そのための領域を確保する。いい加減なやり方としてはメモリにランダムにアクセスして、その周辺をスタック領域として使うやり方である。これは使えるメモリの領域が不明だったり、すでに使われている領域と被ってしまう可能性があるので避けるべき。
そこで今回は、カーネル用に固定した領域を確保する方法を取る。具体的には、未初期化領域を表す bss segment に適当なサイズを確保し、そこをカーネルが使うスタック領域とする。掲載されているコードを loader.s に追加してみた。
3.2 Calling C Code From Assembly
アセンブリから C プログラムを呼び出す方法について。スタックに push
してから call
すればよい。この先、頻繁に用いるメモリレイアウトがあるらしく構造体を定義したほうがよいとのこと。そこで、以下のようなコードを書いて kmain.c とした。
struct example { unsigned char config; /* bit 0 - 7 */ unsigned short address; /* bit 8 - 23 */ unsigned char index; /* bit 24 - 31 */ } __attribute__((packed)); void kmain() { }
3.3 Compiling C Code
普通の C プログラムをコンパイルするのとカーネルをコンパイルするのでは前提が全然違うので、gcc のオプションを色々付けるとのこと。
3.4 Build Tools
掲載されている通りに Makefile ファイルを作った。すると make run
でコンパイルからエミュレータの起動まで一気にできるようになった。これは楽ちん。
所感
パート1はいったんここで終わり。トラブルシューティングなどの必要もあり、調べながら進めたのでここまでの内容はそれなりに分かった。わりと丁寧に書かれたチュートリアルだと思うが、Bochs の実行方法が書かれていなかったり、object file やレジスタに関する基礎知識は前提とされているように感じた。調べながら進めれば問題ない内容にはなっていると思うので大きな問題ではないが。ぼちぼち進めていこう。