簡體   English   中英

GCC -O2 和 __attribute__((弱))

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

看起來帶有-O2__attribute__((weak)) GCC 會根據您引用弱符號的方式產生不同的結果。 考慮一下:

$ 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

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

$ ./測試

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

$ ./測試

0
1
1

問題是,為什么最后一個“./test”產生“0 1 1”,而不是“1 1 1”?

gcc 版本 5.4.0 (GCC)

看起來在進行優化時,編譯器在聲明為const符號和在同一編譯單元中具有weak定義時遇到了麻煩。

您可以創建一個單獨的 c 文件並將 const 弱定義移到那里,它將解決這個問題:

弱定義文件

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

此問題中描述的相同問題: GCC weak attribute on constant variables

概括:

弱符號只有在您不將它們初始化為值時才能正常工作。 鏈接器負責初始化(如果不存在同名的正常符號,它總是將它們初始化為零)。

如果您嘗試將弱符號初始化為任何值,甚至像 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 編譯器不能對其值做出任何假設。 相反,它會像往常一樣生成獲取符號地址的代碼——這就是正常/弱符號查找魔術發生的地方。

這也意味着弱符號只能“自動初始化”為零,而不是任何其他值,除非被同名的普通初始化符號“覆蓋”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM