私の問題は次のように要約できます。
bar.c:
#include <stdio.h>
void bar() {
printf("bar\n");
}
main.c:
#include <stdio.h>
void __attribute__((weak)) bar() {
printf("foo\n");
}
int main() {
bar();
return 0;
}
Makefile:
all:
gcc -c bar.c
ar -rc libbar.a bar.o
gcc main.c -L. -lbar
出力:
$ ./a.out
foo
したがって、bar.cが静的ライブラリlibbar.aのmain.cにリンクされているため、main.cの弱いシンボルbarがbar.cの強いシンボルによって上書きされることはありません。
Libbar.aの強いシンボルをmain.cの弱いシンボルに上書きするようにgccに指示するにはどうすればよいですか?
一般的に言えば、弱い実装をmain
に入れないと、リンカーは実行時についにそれを解決します。ただし、_main.c
_で実装した場合、この静的をリンクするときにのみ、強い境界(_bar.c
_)でオーバーライドできます。
読んでください http://www.bottomupcs.com/libraries_and_the_linker.html -このトピックに関する興味深いものがたくさん含まれています。
私は自分でテストを行いました:
bar.c
_#include <stdio.h>
void bar()
{
puts("bar.c: i'm the strong bar()");
}
_
baz.c
_#include <stdio.h>
void __attribute__((weak)) bar()
{
puts("baz.c: i'm the weak bar()");
}
_
main.c
_#include <stdio.h>
#ifdef V2
void __attribute__((weak)) bar()
{
puts("main: i'm the build in weak bar()");
}
#else
void __attribute__((weak)) bar();
#endif
int main()
{
bar();
return 0;
}
_
私のMakefile:
_all:
gcc -c -o bar.o bar.c
gcc -shared -fPIC -o libbar.so bar.o
gcc -c -o baz.o baz.c
gcc -shared -fPIC -o libbaz.so baz.o
gcc -o main1 main.c -L. -lbar -lbaz
gcc -o main2 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. ./main1 # => bar.c
LD_LIBRARY_PATH=. ./main2 # => baz.c
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main1 # => baz.c (!!)
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main2 # => baz.c
gcc -o main3 main.c bar.o baz.o
gcc -o main4 main.c baz.o bar.o
./main3 # => bar.c
./main4 # => bar.c
gcc -DV2 -o main5 main.c -L. -lbar -lbaz
gcc -DV2 -o main6 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. ./main5 # => main's implementation
LD_LIBRARY_PATH=. ./main6 # => main's implementation
gcc -DV2 -o main7 main.c -L. -lbar -lbaz
gcc -DV2 -o main8 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main7 # => main's implementation
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main8 # => main's implementation
gcc -DV2 -o main9 main.c -L. -lbar -lbaz
gcc -DV2 -o main10 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main9 # => main's implementation
LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main10 # => main's implementation
gcc -c bar.c
gcc -c baz.c
gcc -o main11 main.c bar.o baz.o
gcc -o main12 main.c baz.o bar.o
./main11 # => bar.c
./main12 # => bar.c
gcc -o main13 -DV2 main.c bar.o baz.o
gcc -o main14 -DV2 main.c baz.o bar.o
./main13 # => bar.c
./main14 # => bar.c
_
Main1 && main2を見てください...弱い実装を_main.c
_に入れずに、弱い実装をライブラリに、強い実装を別のライブラリに保持すると、オーバーライドできます。強いライブラリがbar()
の強力な実装を定義している場合は弱いもの。
私はmax.haredoomによって与えられた答えに戸惑っています(そしてそれが受け入れられたこと)。答えは共有ライブラリと動的リンクを扱っていますが、質問は明らかに静的ライブラリを使用した静的リンクの動作に関するものでした。これは誤解を招くと思います。
静的ライブラリをリンクするとき、ld
はnot弱い/強いシンボルを気にしますデフォルト:それは単に未定義のシンボルを最初に遭遇したシンボルに解決します(コマンドラインでの静的ライブラリの順序は重要です)。
ただし、このデフォルトの動作は、--whole-archive
オプションを使用して変更できます。 Makefileの最後のステップを次のように書き直す場合:
gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive
次に、次のように表示されます。
$ ./a.out
bar
簡単に言うと、--whole-archive
は、リンカーにすべてのシンボル(既に解決されているシンボルを含む)をスキャンするように強制します。 (私たちの場合のように)弱いシンボルによってすでに解決された強いシンボルがある場合、強いシンボルは弱いシンボルを無効にします。
静的ライブラリとそのリンクプロセスに関するすばらしい投稿も参照してください "静的リンクのライブラリ順序" by Eli Bendersky および this SO question 。