如何在静态库中使用gcc链接强符号来覆盖弱符号?

我的问题可归纳如下:

bar.c

#include  void bar() { printf("bar\n"); } 

main.c

 #include  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 

所以main.c中的弱符号栏不会被bar.c中的强符号覆盖,因为bar.c被链接到静态库libbar.a中的main.c.

我怎么能告诉gcc在libbar.a中使用强符号来覆盖main.c中的弱符号?

一般来说:如果你没有将弱实现放入main ,链接器最终会在运行时解析它。 但是如果你在main.c实现它,你只能在链接这个静态时用强绑定( bar.c )覆盖它。

请阅读http://www.bottomupcs.com/libraries_and_the_linker.html – 它包含很多关于此主题的有趣内容。

我自己做了一个测试:

bar.c

 #include  void bar() { puts("bar.c: i'm the strong bar()"); } 

baz.c

 #include  void __attribute__((weak)) bar() { puts("baz.c: i'm the weak bar()"); } 

main.c中

 #include  #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但是把一个弱的实现保存在库中而将强的那个保存在另一个库中,你将能够覆盖弱的一个。如果强大的lib定义了bar()的强大实现。

我对max.haredoom给出的答案感到困惑(并且它已被接受)。 答案涉及共享库和动态链接,而问题显然是关于使用静态库的静态链接的行为。 我认为这是误导。

链接静态库时, ld 默认 关心弱/强符号:它只是将未定义的符号解析为第一个遇到的符号(因此命令行中静态库的顺序很重要)。

但是,可以使用--whole-archive选项更改此默认行为。 如果您重写Makefile中的最后一步,如下所示:

 gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive 

然后你会看到:

 $ ./a.out bar 

简而言之,– --whole-archive强制链接器扫描其所有符号(包括已经解析的符号)。 如果有一个强符号已被弱符号解析(如我们的情况),强符号将否决弱符号。

另请参阅Eli Bendersky关于静态库及其链接过程“静态链接中的库顺序”的精彩post以及此SO问题 。