SNSへはこちら

マイコン向けの省サイズなC言語ライブラリ(newlib)を作る方法

皆さん、gcc を自前ビルドしていますか。僕はしまくっています。PC が新しくなり高スペックになったので、これまで以上に軽いフットワークでコンパイラを作成することができるようになりました。

各種マイコンアーキテクチャ用のC言語開発環境を作るのに、以下のステップを踏むと思います。

  1. binutils をビルド
  2. gcc をビルド(ついでに libiberty やら libgcc やらついてくる)
  3. newlib をビルド(libc やら libm が作られる)
  4. C++ のビルドオプションを付し、もう一度 gcc をビルド (libstdc++ がついてくる)

全てできたら、待ってましたと言わんばかりにマイコンプログラムをビルドするのですが、C言語標準ライブラリのプログラムサイズが重すぎるという問題に出くわす人は多いと思います。特に8ビットや16ビットマイコンを用いる人にはこれは死活問題です。

本記事では、これを解決するための方法を簡単に示します。

既存の省サイズなライブラリ

ARM では Newlib-Nano が提供されています。後に載せるリンクではこのライブラリをビルドするオプションの解析が行われていますので、こちらを見るとほぼ同等の物が作れると思います。

RX マイコンでは、OptLib が提供されています。これは GNU Tools のサイトからダウンロードできる Windows 版の GCC に含まれているもので、本記事のビルドオプションを用いても勝つことのできないほど小さいコードで実装がなされています。

Newlib のビルドオプション

これまでの記事では簡易的にしか書いていませんでしたが(最近までサイズを小さくする手法を知らなかった)、../configure をする際にどのようなオプションを付けていますでしょうか? --disable-werror とか --disable-bootstrap とかだけの人が多いと思います。しかしそれではプログラムサイズが大きくなってしまいます。実際にごく一部ですがソースコードを読んだところ、

  • プログラムサイズよりも実行速度を重視したコード内容になっている
  • アラインメントのチェックなど、実行時安全性を考慮した設計になっている(ここが重い)

等々のことがあるからです。一般的に汎用 OS の動く環境のためのクロスコンパイラならばこのような設計は必要かもしれません。が、マイコンでは(当然実行時安全性は必要だが)このような処理があると所望のプログラムを書けなくなってしまう可能性が大いにあります。リソースが限られているからです。

そこで、これらを排除しコードサイズに対して最適化されたライブラリを作成する configure コマンドをご紹介します。

これがそのコマンドだ

RX マイコンに話を合わせています。なお、このオプションについての説明は、(アーキテクチャに関係なく) newlib/README に記載されています。

これらのオプションは、ARM マイコンに付属する Newlib-Nano というライブラリの解析結果から得たものです。これらの中でも分かるものに関して説明します。

$ ../configure --target=rx-elf --prefix=/usr/local/cross/rx-gnu --disable-werror --disable-bootstrap --disable-newlib-supplied-syscalls --enable-newlib-reent-small --disable-newlib-fvwrite-in-streamio --disable-newlib-fseek-optimization --disable-newlib-wide-orient --enable-newlib-nano-malloc --disable-newlib-unbuf-stream-opt --enable-lite-exit --enable-newlib-global-atexit --enable-newlib-nano-formatted-io --enable-target-optspace  --disable-nls

--disable-newlib-supplied-syscalls

Newlib によって提供されるシステムコール実装を無効化します。

--enable-newlib-reent-small

printf 系列の関数を実行する際に reent という構造体が必要になってくるのですが、こちらをよりサイズの小さなものにします。

--enable-newlib-nano-malloc

C言語標準ライブラリでは malloc を内部で用いています。この実装をより小さくするためのオプションです。

--enable-lite-exit と --enable-newlib-global-atexit

こちらもC言語標準ライブラリに関するものです。関数実行後の開放処理を簡易化したものを使います。

--enable-newlib-nano-formatted-io

各種標準ライブラリの処理を簡易化して、複雑なものはオプションとして分けます。printf 系統では、その実装を iprintf (Integer Print Format)として、整数用のライブラリとして再定義します。浮動小数点数用は -u __printf_float というリンクオプションを付けることで有効化します(生成オブジェクトの prefix によって -u _printf_float など、やや異なってくる)。これにより、浮動小数点数を出力しないシステムでのプログラムサイズを小さくすることが見込まれます。

以上のオプションでビルドするとおおよそ標準ライブラリの占めるコードサイズが半分くらいに減少すると思います。
再度申し上げて置きますが、このコードサイズ最適化は、実行時の安全性を犠牲にすることで可能になるということは知っておいてください。まぁコンパイラやリンカスクリプトがしっかりと考慮されていれば、実行時に安全性を検証する必要はなくなってくると思いますが。

以上です。ぜひ任意のアーキテクチャでお試しください。