組み込みの埋まってるとこ

システム寄りの組み込みCプログラミングBLOG

undefined reference to になるGCC特有の原因

undefined reference to エラーはリンク時に定義されていないシンボルがあるときに発生しますが、 GCCではリンクに関する意外なルールが原因で発生することもあります。

GCCでのシンボル検索とundefined referenceになる原因

GCCではリンクするとき起動時にコマンドラインで渡されたリストの順序に従って関数名や変数名等で未解決になっているシンボルを検索していきます。 そして最後まで辿り着いた時点でまだ未解決のシンボルが残っていればもう一度最初から検索することを繰り返します。

しかしライブラリファイル(.a)に対してはこの検索が1回しか行われないという少し意外なルールが存在します。 これにより、あるライブラリで未解決のシンボルがリストの手前に位置しているライブラリの中で定義されていた場合にそれを発見することができず undefined referenceエラーが発生する原因になります。

下記の図はエラーが発生する具体例です。 libB.aの中に未解決のシンボルを発見したとき、 それを解決するためのライブラリに対して行われる検索はリストの最後に位置しているlibB.aで終了してしまい、libA.aがもう一度検索されることはありません。 そのためリストの前方に位置するlibA.aにシンボルが定義されているにも係わらずundefined referenceエラーが発生します。

GCCでライブラリの中で定義されているシンボルが未定義となる例

もしもこのときlibB.aで未解決のシンボルがオブジェクトファイル(.o)の中で定義されているのであれば、 オブジェクトファイルがリストのどこに位置していたとしても再度最初から検索されるときに発見され、エラーになることはありません。

undefined referenceの解決方法

基本的にはコマンドラインで渡すリストの順序を決めるとき、 シンボルを定義しているライブラリをシンボルを外部参照しているライブラリよりも後方に位置するように並べます。

一般的にはよく参照される汎用的なライブラリである程リストの後方に位置するように並べるのがコツと言えます。 またライブラリは1回しか検索されないルールがあるためここで取りこぼしが起こらないように、 ライブラリファイルはすべてのオブジェクトファイルよりも後方に並べるようにします。

gcc ... objA.o objB.o -lA -lB

稀なケースでAライブラリでBライブラリのシンボルを参照していて、かつBライブラリでAライブラリのシンボルを参照する相互参照になっているケースがあります。この場合は同じライブラリを複数回ライブラリリストに並べることで解決できます。なおライブラリは複数回同じファイルをリンクしてもシンボル重複のエラーにはなりません。

gcc ... objA.o objB.o -lA -lB -lA -lB

相互参照を解決するもう一つ方法としてライブラリのグループ化があります。 グループ化されたライブラリは相互参照になっているシンボルがすべて解決するまで繰り返し検索されるようになります。

ライブラリをグループ化するためには--start-groupと--end-groupでライブラリリストを囲みます。 このオプションはリンカーのオプションであるため、GCCから渡すときは -Wl, による前置きが必要です。

gcc ... objA.o objB.o -Wl,--start-group -lA -lB -Wl,--end-group

ただしグループ化はやり過ぎるとリンクが遅くなることがあるため、 本当に必要なものだけをグループ化するように心がけます。

まとめ

GCCでリンクするときライブラリがリストの順序で1回しか検索されることと、 そのために定義されているシンボルがundefined referenceになるケースを示しました。

またその解決方法として、ライブラリのリストの並べ方による方法とライブラリをグループ化する方法を示しました。