简体   繁体   English

GCC -O2 和 __attribute__((弱))

[英]GCC -O2 and __attribute__((weak))

It looks like GCC with -O2 and __attribute__((weak)) produces different results depending on how you reference your weak symbols.看起来带有-O2__attribute__((weak)) GCC 会根据您引用弱符号的方式产生不同的结果。 Consider this:考虑一下:

$ cat weak.c $ catweak.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" );
    }
}

$ cat test.c $ cat test.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();
}

$ make $ make

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

$ ./test $ ./测试

1
1
1

$ make ADD_FLAGS="-O2" $ make ADD_FLAGS="-O2"

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

$ ./test $ ./测试

0
1
1

The question is, why the last "./test" produces "0 1 1", not "1 1 1"?问题是,为什么最后一个“./test”产生“0 1 1”,而不是“1 1 1”?

gcc version 5.4.0 (GCC) gcc 版本 5.4.0 (GCC)

Looks like when doing optimizations, the compiler is having trouble with symbols declared const and having the weak definition within the same compilation unit.看起来在进行优化时,编译器在声明为const符号和在同一编译单元中具有weak定义时遇到了麻烦。

You can create a separate c file and move the const weak definitions there, it will work around the problem:您可以创建一个单独的 c 文件并将 const 弱定义移到那里,它将解决这个问题:

weak_def.c弱定义文件

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

Same issue described in this question: GCC weak attribute on constant variables此问题中描述的相同问题: GCC weak attribute on constant variables

Summary:概括:

Weak symbols only work correctly if you do not initialize them to a value.弱符号只有在您不将它们初始化为值时才能正常工作。 The linker takes care of the initialization (and it always initializes them to zero if no normal symbol of the same name exists).链接器负责初始化(如果不存在同名的正常符号,它总是将它们初始化为零)。

If you try to initialize a weak symbol to any value, even to zero as OP did, the C compiler is free to make weird assumptions about its value.如果您尝试将弱符号初始化为任何值,甚至像 OP 那样初始化为零,C 编译器可以自由地对其值做出奇怪的假设。 The compiler has no distinction between weak and normal symbols;编译器没有区分弱符号和普通符号; it is all (dynamic) linker magic.这都是(动态)链接器魔术。

To fix, remove the initialization ( = 0 ) from any symbol you declare weak:要修复,请从您声明为弱的任何符号中删除初始化 ( = 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__));

Detailed description:详细说明:

The C language has no concept of a " weak symbol ". C 语言没有“弱符号”的概念。 It is a functionality provided by the ELF file format, and (dynamic) linkers that use the ELF file format.它是由 ELF 文件格式和使用 ELF 文件格式的(动态)链接器提供的功能。

As the man 1 nm man page describes at the "V" section,正如man 1 nm手册页在"V"部分所述,

When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error.当弱定义符号与正常定义符号链接时,正常定义符号的使用不会出错。 When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error.当一个弱未定义符号被链接并且该符号未被定义时,弱符号的值变为零且没有错误。

the weak symbol declaration should not be initialized to any value, because it will have value zero if the process is not linked with a normal symbol of the same name.弱符号声明不应初始化为任何值,因为如果进程未与同名的普通符号链接,它的值将为零。 ( "defined" in the man 1 nm page refers to a symbol existing in the ELF symbol table.) man 1 nm页面中的“定义”是指存在于 ELF 符号表中的符号。)

The "weak symbol" feature was designed to work with existing C compilers. “弱符号”功能旨在与现有的 C 编译器一起使用。 Remember, the C compilers do not have any distinction between "weak" and "normal" symbols.请记住,C 编译器在“弱”和“正常”符号之间没有任何区别。

To ensure this would work without running afoul of the C compiler behaviour, the "weak" symbol must be uninitialized, so that the C compiler cannot make any assumptions as to its value.为了确保这在不违反 C 编译器行为的情况下工作,“弱”符号必须未初始化,以便 C 编译器不能对其值做出任何假设。 Instead, it will generate code that obtains the address of the symbol as usual -- and that's where the normal/weak symbol lookup magic happens.相反,它会像往常一样生成获取符号地址的代码——这就是正常/弱符号查找魔术发生的地方。

This also means that weak symbols can only be "auto-initialized" to zero, and not any other value, unless "overridden" by a normal, initialized symbol of the same name.这也意味着弱符号只能“自动初始化”为零,而不是任何其他值,除非被同名的普通初始化符号“覆盖”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM