GCC -O2 and __attribute__((weak))

感情迁移 提交于 2021-01-02 08:27:10

问题


It looks like GCC with -O2 and __attribute__((weak)) produces different results depending on how you reference your weak symbols. Consider this:

$ cat 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" );
    }
}

$ 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

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

$ ./test

1
1
1

$ 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"?

gcc version 5.4.0 (GCC)


回答1:


Looks like when doing optimizations, the compiler is having trouble with symbols declared const and having the weak definition within the same compilation unit.

You can create a separate c file and move the const weak definitions there, it will work around the problem:

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




回答2:


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. 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:

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". It is a functionality provided by the ELF file format, and (dynamic) linkers that use the ELF file format.

As the man 1 nm man page describes at the "V" section,

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.)

The "weak symbol" feature was designed to work with existing C compilers. Remember, the C compilers do not have any distinction between "weak" and "normal" symbols.

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. 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.



来源:https://stackoverflow.com/questions/43177223/gcc-o2-and-attribute-weak

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!