如何使静态库中的 gcc 链接强符号覆盖弱符号?
Posted
技术标签:
【中文标题】如何使静态库中的 gcc 链接强符号覆盖弱符号?【英文标题】:How to make gcc link strong symbol in static library to overwrite weak symbol? 【发布时间】:2012-10-16 20:25:54 【问题描述】:我的问题可以总结如下:
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;
生成文件:
all:
gcc -c bar.c
ar -rc libbar.a bar.o
gcc main.c -L. -lbar
输出:
$ ./a.out
foo
所以 main.c 中的弱符号 bar 不会被 bar.c 中的强符号覆盖,因为 bar.c 链接到静态库 libbar.a 中的 main.c。
如何告诉 gcc 让 libbar.a 中的强符号覆盖 main.c 中的弱符号?
【问题讨论】:
【参考方案1】:我对 max.haredoom 给出的答案感到困惑(并且它被接受了)。答案涉及共享库和动态链接,而问题显然是关于使用静态库进行静态链接的行为。我认为这是误导。
当链接静态库时,ld
不关心弱/强符号默认情况下:它只是将未定义的符号解析为第一次遇到的符号(所以命令行中静态库的顺序很重要)。
但是,可以使用--whole-archive
选项更改此默认行为。如果您将 Makefile 中的最后一步改写如下:
gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive
然后你会看到:
$ ./a.out
bar
简而言之,--whole-archive
强制链接器扫描其所有符号(包括已解析的符号)。如果有一个强符号已经被弱符号解析(如我们的例子),那么强符号将推翻弱符号。
另请参阅有关静态库及其链接过程 "Library order in static linking" by Eli Bendersky 和 this SO question 的精彩帖子。
【讨论】:
谢谢! +1 用于澄清静态链接。请不要问我为什么忽略静态链接... 这对我有用,但是,我查看了manual,我认为应该注意--whole-archive
选项还添加了给定库中的所有符号,这将解决这个问题,但会增加可执行文件的大小,并可能产生额外的链接错误。
针对上述情况,使用 -Wl,--whole-archive 包含要完全扫描的库,然后使用 -Wl,--no-whole-archive 将其关闭。如示例中所示,这将仅包括您指定的库。 (不是所有的库)【参考方案2】:
一般来说:如果您不将弱实现放入您的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()
的强实现,则为弱库。
【讨论】:
谢谢。将所有弱实现分离到另一个库中是一种解决方案。 互联网上唯一提到这个重要的__attribute__((weak))问题。 谢谢!你看这是一些反复试验。但这个问题真的很有趣。以上是关于如何使静态库中的 gcc 链接强符号覆盖弱符号?的主要内容,如果未能解决你的问题,请参考以下文章