5

它看起来像 GCC,-O2__attribute__((weak))根据您引用弱符号的方式产生不同的结果。考虑一下:

$猫弱.c

#include <stdio.h>

extern const int weaksym1;
const int weaksym1 __attribute__(( weak )) = 0;

extern const int weaksym2;
const int weaksym2 __attribute__(( weak )) = 0;

extern int weaksym3;
int weaksym3 __attribute__(( weak )) = 0;

void testweak(void)
{
    if ( weaksym1 == 0 )
    {
        printf( "0\n" );
    }
    else
    {
        printf( "1\n" );
    }

    printf( "%d\n", weaksym2 );


    if ( weaksym3 == 0 )
    {
        printf( "0\n" );
    }
    else
    {
        printf( "1\n" );
    }
}

$猫测试.c

extern const int weaksym1;
const int weaksym1 = 1;

extern const int weaksym2;
const int weaksym2 = 1;

extern int weaksym3;
int weaksym3 = 1;

extern void testweak(void);

void main(void)
{
    testweak();
}

$ 制作

gcc  -c weak.c
gcc  -c test.c
gcc  -o test test.o weak.o

$ ./测试

1
1
1

$ 使 ADD_FLAGS="-O2"

gcc -O2 -c weak.c
gcc -O2 -c test.c
gcc -O2 -o test test.o weak.o

$ ./测试

0
1
1

问题是,为什么最后一个“./test”产生“0 1 1”,而不是“1 1 1”?

gcc 版本 5.4.0 (GCC)

4

2 回答 2

3

看起来在进行优化时,编译器在声明符号constweak在同一编译单元中定义符号时遇到问题。

您可以创建一个单独的 c 文件并将 const 弱定义移到那里,它将解决该问题:

弱定义.c

const int weaksym1 __attribute__(( weak )) = 0;
const int weaksym2 __attribute__(( weak )) = 0;

此问题中描述的相同问题:常量变量上的 GCC 弱属性

于 2017-04-03T07:11:59.610 回答
2

概括:

弱符号只有在您不将它们初始化为值时才能正常工作。链接器负责初始化(如果不存在同名的普通符号,它总是将它们初始化为零)。

如果您尝试将弱符号初始化为任何值,甚至像 OP 那样初始化为零,C 编译器可以自由地对其值做出奇怪的假设。编译器不区分弱符号和普通符号;这都是(动态)链接器魔术。

= 0要解决此问题,请从您声明为弱的任何符号中删除初始化 ( ):

extern const int weaksym1;
const int weaksym1 __attribute__((__weak__));

extern const int weaksym2;
const int weaksym2 __attribute__((__weak__));

extern int weaksym3;
int weaksym3 __attribute__((__weak__));

详细说明:

C 语言没有“弱符号”的概念。它是由 ELF 文件格式和使用 ELF 文件格式的(动态)链接器提供的功能。

正如man 1 nm手册页在该"V"部分中描述的那样,

当弱定义符号与正常定义符号链接时,使用正常定义符号不会出错。当链接一个弱未定义符号且该符号未定义时,该弱符号的值变为零且没有错误。

弱符号声明不应初始化为任何值,因为如果进程未与同名的普通符号链接,它将具有零值。(页面中的“定义”man 1 nm指的是存在于 ELF 符号表中的符号。)

“弱符号”功能旨在与现有的 C 编译器一起使用。请记住,C 编译器在“弱”和“正常”符号之间没有任何区别。

为确保这在不违反 C 编译器行为的情况下工作,“弱”符号必须未初始化,以便 C 编译器无法对其值做出任何假设。相反,它将像往常一样生成获取符号地址的代码——这就是正常/弱符号查找魔术发生的地方。

这也意味着弱符号只能“自动初始化”为零,而不是任何其他值,除非被同名的普通初始化符号“覆盖”。

于 2017-04-03T08:07:01.937 回答