Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 555
user3234859 Avatar asked Apr 03 '17 05:04

user3234859


People also ask

What is the use of weak function attribute in GCC?

Here is the GCC manual extract for the weak function attribute. weak The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions that can be overridden in user code, though it can also be used with non-function declarations.

Is __attribute__ for C++ GCC specific?

Apparently this is because __attribute__ is gcc specific. I am trying to compile some headers that I created on my Linux box using gcc. How can I force Visual C++ to use gcc's __attribute__ mechanism?

What does the weak attribute do in C++?

From the gcc doc manual: weak. The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations.

Why can’t I have two strongly named functions in GCC?

In GCC it’s possible to have weak and strong function attributes. Two strongly names functions cannot be present in code objects being linked. This would be an error produced by the linker – it doesn’t know how to resolve two symbols with the same name.


2 Answers

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

like image 133
nnn Avatar answered Nov 15 '22 07:11

nnn


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.

like image 24
Nominal Animal Avatar answered Nov 15 '22 09:11

Nominal Animal