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

6ovsh4lw  于 2023-02-11  发布在  其他
关注(0)|答案(3)|浏览(195)

我的问题可以归纳如下:

小节c

#include <stdio.h>

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

主文件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. c中的强符号覆盖,因为bar. c链接到静态库libbar. a中的main. c。
我怎样才能让gcc在libbar. a中使用强符号来覆盖main.c中的弱符号?

xdyibdwo

xdyibdwo1#

我对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 Benderskythis SO question的文章。

kfgdxczn

kfgdxczn2#

一般来说:如果你没有在main中实现一个弱的实现,那么链接器最终会在运行时解析它,但是如果你在main.c中实现它,那么在链接这个静态变量时,你只能用一个强绑定(bar.c)覆盖它。
请阅读https://bottomupcs.com/ch09s05.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;
}

我的生成文件:

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()的强实现,你就可以覆盖弱实现。

wpcxdonn

wpcxdonn3#

您应该将弱实现分离到另一个库中。
只在main中声明它。

相关问题