モジュールAとモジュールBがあったとします。(具体的には .a ファイルです)
両方に同じ関数名で func1 func2 func3が定義されています。
両方とも、それぞれのモジュールの中から呼び出されています。
これをビルドすると、何故かリンクエラーになりません。
試しに、モジュールBの func2をコメントアウトすると
多重定義のリンクエラーが出ます。
weak symbolは使っていません。
納得ができないのですが、説明できる方 いますでしょうか?
環境は、linuxでgccです。
こまかい情報がないので予想で書いていきます。
a.c
//void func1(void); void func2(void); void func3(void); //void func1(void){ // func2(); //} void func2(void){ func3(); } void func3(void){ func3(); }
b.c
void func1(void); void func2(void); void func3(void); void func1(void){ func2(); } void func2(void){ func3(); } void func3(void){ func3(); }
$ cat hoge.c extern void func1(void); extern void func2(void); extern void func3(void); int main(){ func1(); func2(); func3(); return 0; }
$ gcc -c a.c b.c $ ar rcs liba.a a.o $ ar rcs libb.a b.o $ gcc -Wall -o hoge hoge.c -L./ -la -lb .//libb.a(b.o): In function `func2': b.c:(.text+0xd): multiple definition of `func2' .//liba.a(a.o):a.c:(.text+0x0): first defined here .//libb.a(b.o): In function `func3': b.c:(.text+0x1a): multiple definition of `func3' .//liba.a(a.o):a.c:(.text+0xd): first defined here collect2: ld はステータス 1 で終了しました
こんな感じでしょうか?
まず初めにgcc(というかld?)の動作として、同名シンボルは先優先というルールがあります。
(コンパイラ・リンカによってはこの辺のルールが違う場合もあったと記憶しています。)
よって、上記の例の場合main()->func1()と呼ばれて、リンク時はfunc1()を見にいきますがliba.aには無いのでlibb.bを見にいきます。
ここでfunc2()を探した場合、liba.aとlibb.aの両方が見つかりリンクエラーになるんだと思います。
a.cのコメントアウトを外すと、main()->func1()でliba.aでfunc1()が見つかります。その後func2()、func3()も同じライブラリ内で見つかります。
ここ以降のlibb.aは先優先のルールにより無視されますので多重定義にはならないと。
ちょっと余計な話…。
このルールによりややこしくなるのが、デフォルトで存在するライブラリ(/usr/lib/とか)です。
開発をしていて知らずにデフォルトのライブラリにある関数を自前で作成して、その関数にバグがあったりするとバグを見つけるのに一苦労w。
まあこの場合だとソースから見つけれるので良いのですが、どこかのメーカが作成したソースコード無しのライブラリでこれをやられると本当に分からないです。
lddとかstringsとかでHackするしかないです。
参考
コメント(0件)