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

use*_*342 23 c gcc ld weak-linking

我的问题可归纳如下:

bar.c:

#include <stdio.h>

void bar() {
    printf("bar\n");
}
Run Code Online (Sandbox Code Playgroud)

main.c:

#include <stdio.h>

void __attribute__((weak)) bar() {
    printf("foo\n");
}

int main() {
    bar();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Makefile:

all:
    gcc -c bar.c
    ar -rc libbar.a bar.o
    gcc main.c -L. -lbar
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./a.out
foo
Run Code Online (Sandbox Code Playgroud)

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

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

小智 31

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

当链接静态库,ld没有在意弱/强符号默认:它只是解决了一个未定义符号的第一个遇到的符号(所以静态库的命令行的顺序很重要).

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

gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive
Run Code Online (Sandbox Code Playgroud)

然后你会看到:

$ ./a.out
bar
Run Code Online (Sandbox Code Playgroud)

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

另请参阅Eli Bendersky关于静态库及其链接过程"静态链接中的库顺序"的精彩帖子以及此SO问题.

  • 这对我有用,但是,我看过 [手册](ftp://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_3.html),我认为应该注意的是,`--whole-archive` 选项还会添加给定库中的所有符号,这将解决这个问题,但会增加可执行文件的大小并可能产生额外的链接错误。 (2认同)

max*_*oom 8

一般来说:如果你没有将弱实现放入你的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()");
}
Run Code Online (Sandbox Code Playgroud)

baz.c

#include <stdio.h>

void __attribute__((weak)) bar() 
{
        puts("baz.c: i'm the weak bar()");
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

我的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
Run Code Online (Sandbox Code Playgroud)

看一下main1 && main2 ...如果你没有把任何弱实现放入main.c库中而将弱者保留在库中,而将强者放在另一个库中,你就能够覆盖弱者如果强者lib定义了一个强大的实现bar().

  • 谢谢。将所有弱实现分离到另一个库中是一种解决方案。 (2认同)