2010年04月26日
GCCでリンク時に不要なコードとデータを削除する方法
これもまた、ELC(Embedded Linux Conference)でのセッションの紹介です。
ELCのスライドのページのDenys Vlasenkoさんの"Link time dead code and data elimination using GNU toolchain"を見てください。
組み込みシステムではやはりコードやデータのサイズの削減が必要になる場面がしばしばあります。これを毎回手作業でやるのではscaleしません。このセッションではGNU Tool chainでこれを行う方法を紹介しています。
必要なバージョンはgcc 4.x 以上、binutils 2.18以上。
gcc 4.5から入ったlto(link time optimization)はこのセッションの話とは別です。
通常はリンクするときにコードは全て.text セクションに集められてリンクされます。リンカではセクションは一塊で扱うので、実際に使用していない関数があってもそれを取り除くことはできません。
しかし、コンパイル時に以下のようなオプションをつけると、関数やデータはそれぞれ別々のセクションに置かれるようになります。
gcc -ffunction-sections -fdata-sections
そして、リンク時に以下のオプションをつけます。
ld --gc-sections
ld直接でなく、gcc経由でldを呼び出すときには以下のようなオプションをつけます。
gcc -Wl,--gc-sections
このようにすると、リンカはガベージコレクタのような動作でどこからも参照されていないセクションを削除します。
ただし、落とし穴もあって"Magic section"という特殊なデータの持ち方をしていると、必要なデータまで削除してしまい、正常動作しないことがあるそうです。glibcをスタティックリンクするときにこれが発生するそうです。ダイナミックリンクならば大丈夫。具体例がスライドで紹介されています。
LinuxカーネルでもこのMagic sectionがあるそうです。この問題の回避方法はlinker scriptの中でKEEP命令を使って明示的にこのセクションを削除するなと指示することだそうです。
例えば、.smp_locks というセクションが削除されるのを防ぐためには
.text: { *(.text) *(.text.*) *(.smp_locks) }
これのlinker scriptに以下のようにKEEP命令を追加します。
.text: { *(.text) *(.text.*) KEEP(*(.smp_locks)) }
これでどれだけ小さくなるかはもちろんプログラムによりますが、1〜8%削減された例がスライドで紹介されています。
追記 (2013.8.28)
ROM化するプログラムの場合はベクターテーブルは当然KEEPをつける必要があります。
また、どのセクションが削除されたかはマップファイルで確認できます。
マップファイルを出力するにはリンク時に -Wl,-Mapオプションをつけます。
gcc ... -Wl,-Map=filename.map
トラックバックURL
コメント一覧
サイズ削減は組み込みシステム開発では常につきまといますよね。私はgcc 4.5で入ったltoとの組み合わせでさらなる効果を期待しています。
さらに我田引水ですが、arm では c99 の構文も
一部導入されているようです。gcc の最新でも入ってきているかと思いますが、
cx++ だけだったかもしれませんが、restrict という keyword の導入に
期待しています。さらなる最適化余地が生まれることを、