torutkのブログ

ソフトウェア・エンジニアのブログ

GCCで、__attribute__ にconstructor指定した関数内から他のライブラリを呼び出すとき

Binary Hack本で記載されていたGCCの__attribute__を使ってconstructor指定した関数は、その実体を持つ動的リンクライブラリファイルをロードしたときに実行されます(main関数が呼ばれる前)。

namespace charlie {
void init() __attribute__((constructor));
void init() {
    std::cout << "charlie::init()" << std::endl;
    // 初期化処理
}
}

例えば上記のコードを持つ動的リンクライブラリlibcharlie.soを作成し、main関数を持つコードとリンクすると、charlie::initが先に実行されて、mainが実行されます。

main.o ---> libcharlie.so

これはなかなか便利な機能ですが、扱い上の注意点もあります。constructor指定された関数から、別な動的リンクライブラリファイルの関数を呼び出したときに、動的リンクライブラリのロード順番によってはSegmentation Faultで落ちたりします。

例えば、上記のlibcharlie.soのcharlie::init()から、libdelta.soの関数 delta::getValue()を呼び出していたとします。

namespace charlie {
void init() __attribute__((constructor));
void init() {
    std::cout << "charlie::init()" << std::endl;
    // 初期化処理
    myValue = delta::getValue();
}
}

このとき、libcharlie.soを生成するときに、libdelta.soを明示的にリンクするリンカ指定をしていないでmain.oから実行ファイルをリンクして生成するときにlibcharlie.soとlibdelta.soをリンクしていたとします。

$ g++ -shared charlie.o -o libcharlie.so
$ g++ main.o -lcharlie -ldelta

ここで、delta::getValue()関数の呼び出しは行われますが、その中でlibdelta.soの静的データ領域を参照していたら、まだ、libdelta.soの初期化が完了していないため、エラー(Segmentation fault)が発生します。

libcharlie.soを生成するときに、libdelta.soをリンクするよう指定していれば、libcharlie.soをリンクする時に、__attribute__のconstructor指定関数があっても問題なく実現されます。

$ g++ -shared charlie.o -ldelta -o libcharlie.so
$ g++ main.o -lcharlie

または、リンク順番を変えて、先に依存するlibdelta.soを、次にlibcharlie.soをリンクする指定にすれば動作します。

最初、libdelta.soの関数が呼び出せるので、リンクの問題だとは思わなかったので、原因に至るまで悩みました。