情弱ログ

参考にならないので当てにしないでください

12ステップOS自作本の開発環境をGentoo Linux + crossdev + gcc 7.3.0で構築した話

10月の初旬に「12ステップで作る組込みOS自作入門」という本を買いました。

Amazon CAPTCHA

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にて説明がされているので、そちらを参照してください。

ソースコードgithubに上げています。

github.com

Gentoo Linux + crossdevという冗談はさておき、gcc 7.3.0でも数箇所の変更で動作することが確認できました。
もちろん、Gentoo Linuxをインストールしてみるのも一興だと思います。また、もしもGentoo Prefix上に環境を構築できればMacOSでもできるのではないかなと思います。

サボらず頑張れば1ヶ月もかからずに実績解除できるので、冬休みのお供にいかがでしょうか。