NetBSD libc を選択した理由
文責 京都マイクロコンピュータ(株)exeGCC 開発責任者 若槻俊宏 2020/12/2
注釈
本項は技術ドキュメントではなく読み物となります。個人の主観が入るため、文責を明記してあります。KMC 全体の意見を代表するものではありません。
組み込み向けの GCC ツールチェーンでは newlib を使用するのが一般的で、exeGCC のように NetBSD の libc を選択したケースは、おそらく他に存在しないのではないかと思います。ここではなぜ exeGCC が NetBSD libc を選択したのか、その理由を解説します。
libc を自社開発からオープンソースに移行した理由
SOLID 用の exeGCC s シリーズ(sXXX)は最初から NetBSD libc ですが、それ以前の単体製品である exeGCC4 r004 までは、KMC が開発した libc を使用していました。しかし AArch64(64 bit ターゲット)や C99/C11 規格のサポートなどを考えた時、開発コストを抑え品質を高めるためにはオープンソースの採用が不可欠であると判断しました。
歴史的な理由
exeGCC は広範な CPU アーキテクチャ(SH、V85x、68k、ARM、MIPS、PPC など)をサポートし、それぞれのアーキテクチャごとに様々な設定(例えば命令セット、エンディアン、FPU の種類と ABI、PARTNER デバッガと通信するための VLINK 機能など)が異なる膨大なプリビルドライブラリを同梱してきました。また、Windows 上でライブラリをリビルドできるという、一般的なコンパイラツールチェーンにはまずない機能があります。
そのため、以下の要件が求められました。
単一のソースツリーから全パターンのライブラリをビルドできること。
Windows 上でビルドするために GNU ツールへの依存が少ないこと。
r004 以前のバージョンとの互換性をできる限り保てること。
VLINK をサポートするために、共通関数と低水準入出力関数が分離していること。
PARTNER-Jet2 がサポートする、AArch64/ARM/SH をサポートできること。
NetBSD libc はこれらの要件全てを満たしていました
NetBSD の libc は全てのソースコードと Makefile(ソースコードがコンパイル対象となる条件や、コンパイル時に必要なオプション)が単一のソースツリーとして入手、確認できました。
AArch64、ARM、SH をサポートしていました。(NetBSD 7.0 当時はまだ AArch64 が正式サポートでは無かったため、文字列関数のアセンブラ実装にバグがあり、ARM 社が公開している cortex-strings ライブラリで置き換えたりと、多少の苦労はありましたが。)
ソースコードのスタイルも標準的で、共通部分とターゲット依存部分が明確に分離されていました。これにより、まだ AArch64 が正式サポートでは無い段階でも、実績のある部分と要注意の部分を事前に見積もることが可能なので安心感がありました。
非常に豊富な組み込み環境での実績があるので信頼性が期待できました。
open, close, read, write, lseek などの低水準入出力関数をそのまま exeGCC の VLINK サポートコードに置き換えて使用できました。
ライブラリの初期化もシンプルで、既存の exeGCC のリンカスクリプトやスタートアップがほぼそのまま使用できました。
組み込み向けかつ、本物の UNIX の libc という魅力がありました。組み込み向けの libc はどうしても locale やワイド文字、マルチバイト文字のサポートなど、通常は使用しない部分の実装が最低限のダミー関数的なものになります。
これは現状では無くても問題無いのですが、ちゃんと動いた実績のあるソースコードが豊富に存在し、必要に応じて有効化できることに将来への可能性を感じました。例えば将来的に SOLID OS が POSIX をサポートすることになった場合に、素直にライブラリを拡張して行くことができそうです。(現時点では予定はありません。)
また、(サポート範囲には入っていないので、自己責任で使用していただくことになりますが)BSD 拡張などの非標準関数を利用できるという副次的メリットもあります。過去に BSD の非標準関数(strlcpy など)が欲しいと問い合わせがあったこともあり、特に問題無さそうな string 周りの関数などはできる限りそのまま入れました。また、libc の関数の実装に利用されている線形リストや赤黒木のライブラリなども入っています。
結果的には NetBSD libc を選択したことにより、非常に良好な結果が得られたので、今後も引き続き継続して行く予定です。おそらく次のリリースでは、現時点での最新リリースである NetBSD 9.1 ベースにアップデートしますが、libc 自体は良く枯れているのでほとんど変わっていないことを確認しています。7.0 ではいくつか気になった点があり修正していたような所も、9.1 では修正されていて、単に放置されていただけではなくちゃんとメンテナンスされていることも確認できました。
他の候補との比較
以下ではいろいろ書いていますが、どれも素晴らしい libc であることに間違いありません。たまたま exeGCC の要件と合わなかった、あるいは選定当時の限られた時間での調査だったため、見積り間違いや現在は状況が変わっている可能性も大いにあります。
また、exeGCC に求められる要件自体も変わってきている(それほど多くのアーキテクチャやターゲットは求められず、収斂しつつあるなど)ため、今度以下のライブラリ、特に newlib を選択する可能性は大いにあります。実際過去には特定顧客向けの個別案件で、newlib を使用した exeGCC を開発・販売した実績もあります。
newlib
newlib は configure によるソースコードの生成が必要で、それによりどのソースコードが生成され、生成されたソースファイルにどのようなターゲット依存性が発生するのか、その見極めが困難に思えました。これが一番大きな理由で、以後の理由は補足的なものです。
同じ理由により、Windows 上でのビルドの確立も困難であることが予想されました。
選定当時の newlib 2.2.0 のソースコードはマクロを多用した独特なスタイルで記述されていて、通常の C プログラムとはかけ離れた見た目になっていました。(現在の 3.3.0 では _DEFUN() のようなマクロは廃止されたようです。)
見た目だけなら良いのですが、リエントラント性を確保するためのフレームワークが根底にあるため、exeGCC の既存コードとマージする際には多くの修正が必要と思われました。
newlib には libc と libgloss(低水準入出力関数)が明確に分かれているという強みがありますが、既存の exeGCC とは少し方針が異なり、互換性を保つための変更が大きくなることが予想されました。exeGCC は open や read のレベル、あるいはもっと上のレベルで差し替えていますが、newlib はさらに内部で使用される _open や _read など libgloss 関数部分だけを差し替える方針です。newlib の方針は、libc.a 部分を共通化できるメリットがありますが、specs ファイルやリンカスクリプト、セットアップツールなどの全体的な修正が必要になるので、当時のできる限り工数を減らして短期間で開発したいという要件とは合いませんでした。
ただし、ユーザが使用する機会はありませんが、GCC の testsuite を通す際に、newlib が実装している GDB sim や QEMU 向けのシステムコール関数を移植し、VLINK サポート関数部分と差し替えて使用しています。また、SOLID においてはセットアップツールは使用せず、SOLID IDE やプロジェクトファイルによる柔軟な管理が可能になっているので、上手く整理すれば newlib の実装にすり合わせることは可能と思われます。(SOLID 2.0 からベアメタル対応によりターゲットが大きく増えたため、ツールチェーンの容量削減のために、これらの工夫も必要になってくる可能性は大いにあります。)
FreeBSD/OpenBSD
これらはサーバ向けやデスクトップ向けというイメージがあったのと、SH のサポートがありませんでした。SH がディスコンとなり久しい昨今ならば、これらの採用もあったかもしれません。
ただしこれらは全て共通の BSD libc を先祖に持ち、ライセンスの問題も無いため、お互いに開発成果を取り込みあっているため、断絶しているわけではありません。事実 OpenBSD(NetBSD からの fork)の strlcpy 関数などが NetBSD の libc にファイルそのままのレベルで取り込まれています。
MUSL
MUSL は基本的に Linux 用の libc で、低水準入出力関数が分離しておらず、直接 Linux のシステムコールが呼ばれている所がありました。詳しく調査していないため、比較的容易に分離できる可能性もありますが、当時は候補に入りませんでした。
他にも bionic などもありますが、これは OpenBSD libc を Android(Linux)用に改良したものということで、詳しくは調査していません。
また全てがスタティックリンクされることが前提の組み込み向けツールチェーンなので、MIT ライセンスや(2 条項)BSD ライセンス(およびそれらと同等かより緩いライセンス)の libc 以外は候補にはなりませんでした。