背景
普段、私はデータサイエンティストとして働く中でデータベースをよく触ります。仕組みなどを勉強していくうちに効率的なメモリ管理や高速化に興味を持ちだしました。興味の先はそこからさらに低レイヤーに下り、PCがどのようにスクリプトを認識しているかCPUはどのような機能があるかOSとは何かなど知ってみたいという気持ちになりました。そこで今回はあの有名な「ゼロからのOS自作入門」を購入するに至りました。
「ゼロからのOS自作入門」 は、内田公実さんによる著書で、OS(オペレーティングシステム)の設計と実装を通じて、コンピュータの仕組みや低レベルプログラミングを学べる本です。PC/AT互換機(x86アーキテクチャ)を対象に、C言語やアセンブリ言語を使い、シンプルなOSを一から作成するプロセスを解説しています。
本作の第一章「PCの仕組みとHello World」を経て、私の学びになった部分を記していきたい。
- 実行可能ファイル
- C言語
結果
ひとつひとつ打ち込んで作ったバイナリファイルをQEMUを使って、表示させることができた。
これに至る流れを説明する。まず電源を入れると、UEFI BIOSが動く。コンピューターを初期化し、接続されているストレージを探索し、実行可能ファイルを見つけるとメモリに呼び出す。その後、BIOSの実行を中断し、読みだしたファイルの実行を開始する。
以下、結果に至るまでの学びを記す。
学び① 実行可能ファイル
最初に実行可能ファイルの中身を以下に示す。
details
00000000: 4d5a 0000 0000 0000 0000 0000 0000 0000 MZ.............. 00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000030: 0000 0000 0000 0000 0000 0000 8000 0000 ................ 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000080: 5045 0000 6486 0200 0000 0000 0000 0000 PE..d........... 00000090: 0000 0000 f000 2202 0b02 0000 0002 0000 ......"......... 000000a0: 0002 0000 0000 0000 0010 0000 0010 0000 ................ 000000b0: 0000 0040 0100 0000 0010 0000 0002 0000 ...@............ 000000c0: 0000 0000 0000 0000 0600 0000 0000 0000 ................ 000000d0: 0030 0000 0002 0000 0000 0000 0a00 6081 .0............`. 000000e0: 0000 1000 0000 0000 0010 0000 0000 0000 ................ 000000f0: 0000 1000 0000 0000 0010 0000 0000 0000 ................ 00000100: 0000 0000 1000 0000 0000 0000 0000 0000 ................................
00000180: 0000 0000 0000 0000 2e74 6578 7400 0000 .........text...
00000190: 1400 0000 0010 0000 0002 0000 0002 0000 ................
000001a0: 0000 0000 0000 0000 0000 0000 2000 5060 ............ .P`
000001b0: 2e72 6461 7461 0000 1c00 0000 0020 0000 .rdata....... ..
000001c0: 0002 0000 0004 0000 0000 0000 0000 0000 ................
000001d0: 0000 0000 4000 5040 0000 0000 0000 0000 ....@.P@........
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000200: 4883 ec28 488b 4a40 488d 15f1 0f00 00ff H..(H.J@H.......
00000210: 5108 ebfe 0000 0000 0000 0000 0000 0000 Q...............
00000220: 0000 0000 0000 0000 0000 0000 0000 0000 ................
................
000003f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000400: 4800 6500 6c00 6c00 6f00 2c00 2000 7700 H.e.l.l.o.,. .w.
00000410: 6f00 7200 6c00 6400 2100 0000 0000 0000 o.r.l.d.!.......
................
000005f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
以上の内容のファイル(.efi)が実行可能ファイルの中身である。
1.1行目「00000000: 4d5a」
DOSの初期開発に関わったプログラマー Mark Zbikowski 氏のイニシャルを表す。
2.9行目「00000080: 5045 0000 6486 0200 0000 0000 0000 0000 PE..d...........」
PE形式であること。6486 0200→x86_64アーキテクチャを示す。ビッグエンディアンだと0x8664となる(上位ビットが先)。
3.19,20行目「00000180: 0000 0000 0000 0000 2e74 6578 7400 0000
00000190: 1400 0000 0010 0000 0002 0000 0002 0000」
セクションとメモリサイズを表す。
4.21行目「00000200: 4883 ec28 488b 4a40 488d 15f1 0f00 00ff 」
機械語でCPUへの命令が記載されている。
以下それぞれの意味。
4883 EC28 ; sub rsp, 0x28 ; スタックポインタ (rsp) を 40 バイト分確保する
488B 4A40 ; mov rcx, [rdx+0x40]; rdx+0x40 のアドレスから rcx に値を移動
488D 15F1 0F00 00 ; lea rdx, [rip+0xff0f1]; rip 相対でのアドレスを rdx にロード
5.41,42行目
「00000400: 4800 6500 6c00 6c00 6f00 2c00 2000 7700
00000410: 6f00 7200 6c00 6400 2100 0000 0000 0000」
「Hello World!」の文字列を表す。
以上が実行可能ファイルについて学んだことである。ヘッダー、引数の設定、文字表示など構成が分かれており、それぞれのバイナリの意味が分かった。またこれをCPUが読み取って、OSを起動している(全然形から遠すぎるが)のだと実感した。
学び② C言語
情報系専攻ではなかったため、学びになった箇所。
C言語→機械後命令→実行可能ファイルという流れ。コンパイルによって、CPUが直接実行できる機械語に変化させていく。次にすべてのオブジェクトファイルをまとめて実行可能ファイル(.efi)としている。.efiをxxdコマンドで見てみると、手打ちしてつくった先述のファイルと同じようなファイルが作成されていた。40行目に文字が入るなどの構成の部分では変わらないことや8行目にx86_64アーキテクチャの指定があったりした。
- .cファイルの中身
以下のファイルがプログラムの本体だそう。
「SystemTable->ConOut…」の一行が「Hello world!」と表示するための心臓部。clang,lldコマンドを用いて実行可能ファイルにしていく。
EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable) {
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello, world!\n");
while (1);
return 0;
}
最後に
第一章「PCの仕組みとHello world!」を通して、よく耳にするC言語が低レイヤー言われる理由を知れた。またOSが起動するまでにどのような処理が内部でされているのか知ることができた。次の第二章ではブートローダをつくる。