首页 > 解决方案 > __attribute__ ((weak)) 不适用于全局变量

问题描述

pqy@localhost ~/src/test/a $ cat m.c
#include <stdio.h>
int aaaaa __attribute__ ((weak)) =8;
int main(void){
    printf("%d\n", aaaaa);
    return 0;
}
pqy@localhost ~/src/test/a $ cat lib.c
int aaaaa = 5;

pqy@localhost ~/src/test/a $ gcc lib.c -fPIC -shared -o libb.so;gcc m.c -o m -L. -lb -Wl,-rpath=$PWD;./m
8

以上是我的代码和测试结果。我很困惑为什么它不能按预期工作。

也尝试功能,而不是以太。下面是测试结果。

pqy@localhost ~/src/test/a $ cat lib.c
int fun() {
    return 5;
}
pqy@localhost ~/src/test/a $ cat m.c
#include <stdio.h>
__attribute__((weak)) int fun() {
    return 8;
}
int main(void){
    printf("%d\n", fun());
    return 0;
}
pqy@localhost ~/src/test/a $ gcc lib.c -fPIC -shared -o libb.so;gcc m.c -O0 -o m -L. -lb -Wl,-rpath=$PWD;./m
8
pqy@localhost ~/src/test/a $ ldd m
        linux-vdso.so.1 (0x00007ffd819ec000)
        libb.so => /home/pqy/src/test/a/libb.so (0x00007f7226738000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f7226533000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f7226744000)
pqy@localhost ~/src/test/a $

标签: linuxgcclinkerweak-symbol

解决方案


归根结底,您在此处观察到的事实是,如果链接器可以静态解析符号,则链接器将不会动态解析符号。看:

主程序

extern void foo(void);
extern void need_dynamic_foo(void);
extern void need_static_foo(void);

int main(void){
    foo();
    need_dynamic_foo();
    need_static_foo();
    return 0;
}

dynamic_foo.c

#include <stdio.h>

void foo(void)
{
    puts("foo (dynamic)");
}

void need_dynamic_foo(void)
{
    puts(__func__);
}

static_foo.c

#include <stdio.h>

void foo(void)
{
    puts("foo (static)");
}

void need_static_foo(void)
{
    puts(__func__);
}

编译源代码:

$ gcc -Wall -c main.c static_foo.c
$ gcc -Wall -fPIC -c dynamic_foo.c

创建一个共享库:

$ gcc -shared -o libfoo.so dynamic_foo.o

并链接一个程序:

$ gcc -o prog main.o static_foo.o libfoo.so -Wl,-rpath=$PWD

它运行如下:

$ ./prog
foo (static)
need_dynamic_foo
need_static_foo

因此fooandneed_static_foo被静态解析为 from的定义,而fromstatic_foo.o的定义忽略,尽管事实上需要并提供了. 如果我们将链接顺序更改为:foolibfoo.solibfoo.so need_dynamic_foo

$ gcc -o prog main.o libfoo.so static_foo.o -Wl,-rpath=$PWD
$ ./prog
foo (static)
need_dynamic_foo
need_static_foo

如果我们替换static_foo.c为:

static_weak_foo.c

#include <stdio.h>

void __attribute__((weak)) foo(void)
{
    puts("foo (static weak)");
}

void need_static_foo(void)
{
    puts(__func__);
}

编译并重新链接:

$ gcc -Wall -c static_weak_foo.c
$ gcc -o prog main.o libfoo.so static_weak_foo.o -Wl,-rpath=$PWD
$ ./prog
foo (static weak)
need_dynamic_foo
need_static_foo

尽管fooin的定义现在被声明为弱,但可以static_weak_foo.c静态解析为该定义的事实仍然抢占了动态解析它的任何需要。foo

现在,如果我们编写另一个包含另一个强定义的源文件 foo

static_strong_foo.c

#include <stdio.h>

void foo(void)
{
    puts("foo (static strong)");
}

并编译它并链接如下:

$ gcc -Wall -c static_strong_foo.c
$ gcc -o prog main.o static_weak_foo.o libfoo.so static_strong_foo.o -Wl,-rpath=$PWD

我们看:

$ ./prog
foo (static strong)
need_dynamic_foo
need_static_foo

现在,libfoo.so仍然提供 的定义need_dynamic_foo,因为没有别的;static_weak_foo.o仍然提供 的唯一定义need_static_foo,而fooin的定义libfoo.so仍然被忽略,因为符号可以静态解析。

但是在这种情况下,有两个foo不同文件中的定义可用于静态解析:弱定义 instatic_weak_foo.o和强定义 in static_strong_foo.o。通过您熟悉的链接规则,强定义获胜。

如果这两个静态链接的定义foo都很强,那么当然会出现多重定义错误,就像:

$ gcc -o prog main.o static_foo.o libfoo.so static_strong_foo.o -Wl,-rpath=$PWD
static_strong_foo.o: In function `foo':
static_strong_foo.c:(.text+0x0): multiple definition of `foo'
static_foo.o:static_foo.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

其中的动态定义libfoo.so 不起作用。因此,您可以遵循以下实用原则:您熟悉的用于在链接中对同一符号的弱定义和强定义进行仲裁的规则仅适用于在没有属性的情况下会引发多重定义错误的 竞争定义。weak


推荐阅读