概要
C++で書かれた、ある静的ライブラリの関数において、他のライブラリ等のどこにも定義されていない関数を呼び出していました。その静的ライブラリをリンクしている実行ファイル作成のためのビルドにおいて、リンクエラーが発生することなく成功していました。
リンクされている静的ライブラリ内で定義されていない関数を呼び出している場合、未解決の外部シンボルが残ったままとなり、リンクエラーになるのでは?と自分は思っていたのですが、間違いでした。
結論を言うと、未解決の参照を定義していない静的ライブラリのオブジェクトファイルはリンクされないということでした。
ソースコードを伴わない説明だと、分かり難いと思うので、以下に検証用の複数のケースのソースコードとその解説を載せます。検証にはClangを用いていますが、他のGCCやVC++等のコンパイラでも挙動としては変わりないと思われます。
検証
ケース1
下記の2つのC++のソースコードがあります。
#include <iostream>
int main()
{
std::cout << "main()" << std::endl;
return 0;
}
void f();
void g()
{
f();
}
hoge.cppのf()
の定義は存在していません。
未解決の外部シンボルのリンクエラーの例として、普通にビルドしてみます。g()
から呼び出しているf()
のシンボルが定義されておらず見つからないという、未解決の外部シンボルのリンクエラーになります。
$ clang++ -c main.cpp
$ clang++ -c hoge.cpp
$ clang++ main.o hoge.o
Undefined symbols for architecture x86_64:
"f()", referenced from:
g() in hoge.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ケース2
ケース1のmain.cppとhoge.cppを再び用います。hoge.cppから作成されるhoge.oをlibhoge.aという静的ライブラリに含めるようにし、下記のようにビルドしてみると、リンクエラーが発生することなく成功します。未解決の参照を定義していないhoge.oがリンクされないからです。
$ clang++ -c main.cpp
$ clang++ -c hoge.cpp
$ ar rcs libhoge.a hoge.o
$ clang++ main.o -L. -lhoge
実行結果は下記です。
$ ./a.out
main()
ケース3
main.cppとhoge.cppのソースコードを少し修正し、main.cppのmain()
から呼び出す関数h()
をhoge.cppに定義します。
#include <iostream>
void h();
int main()
{
std::cout << "main()" << std::endl;
h();
return 0;
}
#include <iostream>
void f();
void g()
{
f();
}
void h()
{
std::cout << "h()" << std::endl;
}
ケース2と同じようにビルドすると、未解決の外部シンボルのリンクエラーが発生します。h()
という未解決の参照をhoge.oが定義しており、hoge.oがリンクされたからです。
$ clang++ -c main.cpp
$ clang++ -c hoge.cpp
$ ar rcs libhoge.a hoge.o
$ clang++ main.o -L. -lhoge
Undefined symbols for architecture x86_64:
"f()", referenced from:
g() in libhoge.a(hoge.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ケース4
ケース3おいてhoge.cpp内に定義した関数h()
を新しく作成するhoge2.cppに移し、libhoge.aにhoge.cppとhoge2.cppのそれぞれから作成されるhoge.oとhoge2.oを含めます。
#include <iostream>
void h();
int main()
{
std::cout << "main()" << std::endl;
h();
return 0;
}
void f();
void g()
{
f();
}
#include <iostream>
void h()
{
std::cout << "h()" << std::endl;
}
下記の手順で無事ビルドに成功しました。
$ clang++ -c main.cpp
$ clang++ -c hoge.cpp
$ clang++ -c hoge2.cpp
$ ar rcs libhoge.a hoge.o hoge2.o
$ clang++ main.o -L. -lhoge
hoge2.oは未解決の参照が定義されているためリンクされますが、hoge.oはそうではないためリンクされないからです。同じ静的ライブラリの中に含まれていても、オブジェクトファイルによってリンクされるかされないかが変わってくるということですね。
実行結果は下記です。
$ ./a.out
main()
h()