12ステップOS自作本の開発環境をGentoo Linux + crossdev + gcc 7.3.0で構築した話
10月の初旬に「12ステップで作る組込みOS自作入門」という本を買いました。
H8というCPUが乗ったマイコンボード上に組込みOSを動かすという内容で、サボりつつも2ヶ月程度で完成させることができました。
かいつまんでやったことを紹介すると、ELFファイルをXMODEMで受信し、RAM上に展開して実行するブートローダの作成を前半6回で行い、システムコールやスレッド管理、スケジューラを持つOSの作成を後半6回で行います。
アセンブリやリンカスクリプトの書き方を始めとし、割り込みやメモリ管理といったOSの基本的な機能まで丁寧に説明しているので、専門外の私でも理解することができました。
実機やシリアルコンソールの購入が必要なため、本と合わせると一万円弱の出費が必要ですが、OS自作というレアな実績が解除できるので非常におすすめです。
ただし、初版の出版が2010年と少し古く、推奨環境がUbuntu9.10やWindows XPといったサポート切れのOSになっています。
筆者のHPで構築済みのVMが配布されているため、これを用いれば問題ないのですが、クロスコンパイラのビルドという一つの大きなタスクが未消化に終わってしまいます。
一方、Gentoo Linuxといえば(運が良ければ)GCCのビルドがコマンド一発でできるディストリビューションとして、一部のクラスタから熱狂的な支持を集めています(多分)。
更にcrossdevというツールを用いるとコマンド一発でクロスビルド環境が整うという、まるで本書のためにあつらえたかのようなディストリビューションです。
そこで今回はGentoo Linux + crossdevでh8300-elf-gcc 7.3.0な環境を構築するまでのメモです。
まず、crossdevのインストールが必要です。
$ sudo emerge crossdev
その後、/etc/portage以下にpackage.env、package.use、package.keywordsというディレクトリを作成します。これらは既に同名のファイルが存在していると思うので、一時的に避難してから作成したディレクトリにコピーします。
$ cd /etc/portage $ sudo mv package.use package.use.back $ sudo mkdir package.env package.use package.keywords $ sudo mv package.use.back package.use/use
私はpackage.useをpackage.use/useというファイルとして移動しましたが、特に問題なく動いています。
これで準備は完了したので、crossdevによってクロスコンパイラをインストールします。
$ sudo crossdev --stable -t h8300 (しばらく放置) $ whereis h8300-elf-gcc h8300-elf-gcc: /usr/bin/h8300-elf-gcc /usr/x86_64-pc-linux-gnu/h8300-elf/gcc-bin/7.3.0/h8300-elf-gcc $ h8300-elf-gcc -v Using built-in specs. COLLECT_GCC=h8300-elf-gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/h8300-elf/7.3.0/lto-wrapper Target: h8300-elf Configured with: /var/tmp/portage/cross-h8300-elf/gcc-7.3.0-r3/work/gcc-7.3.0/configure --host=x86_64-pc-linux-gnu --target=h8300-elf --build=x86_64-pc-linux-gnu --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/h8300-elf/gcc-bin/7.3.0 --includedir=/usr/lib/gcc/h8300-elf/7.3.0/include --datadir=/usr/share/gcc-data/h8300-elf/7.3.0 --mandir=/usr/share/gcc-data/h8300-elf/7.3.0/man --infodir=/usr/share/gcc-data/h8300-elf/7.3.0/info --with-gxx-include-dir=/usr/lib/gcc/h8300-elf/7.3.0/include/g++-v7 --with-python-dir=/share/gcc-data/h8300-elf/7.3.0/python --enable-languages=c --enable-obsolete --enable-secureplt --disable-werror --with-system-zlib --enable-nls --without-included-gettext --enable-checking=release --with-bugurl=https://bugs.gentoo.org/ --with-pkgversion='Gentoo 7.3.0-r3 p1.4' --disable-esp --enable-poison-system-directories --disable-libstdcxx-time --disable-shared --disable-libatomic --disable-threads --without-headers --disable-bootstrap --with-newlib --enable-multilib --disable-altivec --disable-fixed-point --disable-libgomp --disable-libmudflap --disable-libssp --disable-libcilkrts --disable-libmpx --disable-vtable-verify --disable-libvtv --disable-libquadmath --enable-lto --without-isl --disable-libsanitizer --enable-default-pie --enable-default-ssp Thread model: single gcc version 7.3.0 (Gentoo 7.3.0-r3 p1.4)
超簡単ですね。USEフラグの変更は/etc/portage/package.use/cross-h8300-elfから行えます。私はデフォルトオプションのまま12回まで完走できたので、特に変更しなくても良いと思います。
本との差異ですが、4点ほど改変が必要です。
まず、インストールされたファイルは/usr/binに設置されるため、Makefileに変更が必要です。これは全ての回で必要です。
PREFIX = /usr ARCH = h8300-elf BINDIR = $(PREFIX)/bin ADDNAME = $(ARCH)-
次に、サポートページにあるように、ブートローダ側のリンカスクリプトに変更が必要です。
http://kozos.jp/books/makeos/index.html#binutils-new
--- ld.scr~ 2010-02-28 23:23:47.000000000 +0900 +++ ld.scr 2011-10-30 15:16:58.000000000 +0900 @@ -28,10 +28,11 @@ .rodata : { _rodata_start = . ; *(.strings) *(.rodata) *(.rodata.*) + . = ALIGN(4); _erodata = . ; } > rom .data : { _data_start = . ;
また、vector.oのセクションの配置が異なるため、vector.c側で無理矢理対処します。
--- vector.original.c 2018-11-29 00:19:14.260000000 +0900 +++ vector.c 2018-11-28 23:48:58.650000000 +0900 @@ -5,7 +5,7 @@ extern void intr_syscall(void); extern void intr_serintr(void); -void (*vectors[])(void) = { +void (*vectors[])(void) __attribute__((section(".data"))) = { start, NULL, NULL, NULL, NULL, NULL, NULL, NULL, intr_syscall, intr_softerr, intr_softerr, intr_softerr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
これはリンカスクリプトではvector.oの.dataセクションを0x000000にマップするようになっているのですが、gcc 7.3.0でビルドしたvector.oは.dataセクションではなく.data.relセクションにvectors配列を格納してしまうためです。本当は1回目から変更が必要ですが、startのアドレスが0x0であり、割り込みベクタが0x0で初期化されるようなので7回目までは動作します。7回目以降は割り込みが発生する度に0x0へジャンプしてしまうため、ブートローダが立ち上がることになります。
以下は変更前のセクション情報です。.dataセクションのSizeが0になっていることが確認できます。
$ readelf -S vector.o There are 10 section headers, starting at offset 0x36c: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000000 00 AX 0 0 1 [ 2] .data PROGBITS 00000000 000034 000000 00 WA 0 0 1 [ 3] .bss NOBITS 00000000 000034 000000 00 WA 0 0 1 [ 4] .data.rel PROGBITS 00000000 000034 000100 00 WA 0 0 4 [ 5] .rela.data.rel RELA 00000000 00025c 0000cc 0c I 7 4 4 [ 6] .comment PROGBITS 00000000 000134 000023 01 MS 0 0 1 [ 7] .symtab SYMTAB 00000000 000158 0000c0 10 8 7 4 [ 8] .strtab STRTAB 00000000 000218 000044 00 0 0 1 [ 9] .shstrtab STRTAB 00000000 000328 000044 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), p (processor specific)
以下は変更後のセクション情報です。.data.relセクションがなくなり、代わりに.dataセクションが0x100バイトになっていることが確認できます。
$ readelf -S vector.o There are 9 section headers, starting at offset 0x354: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000000 00 AX 0 0 1 [ 2] .data PROGBITS 00000000 000034 000100 00 WA 0 0 4 [ 3] .rela.data RELA 00000000 00024c 0000cc 0c I 6 2 4 [ 4] .bss NOBITS 00000000 000134 000000 00 WA 0 0 1 [ 5] .comment PROGBITS 00000000 000134 000023 01 MS 0 0 1 [ 6] .symtab SYMTAB 00000000 000158 0000b0 10 7 6 4 [ 7] .strtab STRTAB 00000000 000208 000044 00 0 0 1 [ 8] .shstrtab STRTAB 00000000 000318 00003a 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), p (processor specific)
最後に、kozos.cのkz_thread構造体にパディング用のダミーデータが必要です。
Step8(12ステップ本) | Physical Computingに挑戦!
kz_thread構造体の最後にchar dummy[16];というダミーデータを挿入しておきます。なお、8回目は16バイト、9回目以降は8バイトに変更します。これを行わない場合、undefined reference to `___mulsi3'といったエラーが出てビルドに失敗します。
これについてはp.421にて説明がされているので、そちらを参照してください。
Gentoo Linux + crossdevという冗談はさておき、gcc 7.3.0でも数箇所の変更で動作することが確認できました。
もちろん、Gentoo Linuxをインストールしてみるのも一興だと思います。また、もしもGentoo Prefix上に環境を構築できればMacOSでもできるのではないかなと思います。
サボらず頑張れば1ヶ月もかからずに実績解除できるので、冬休みのお供にいかがでしょうか。