GCC -O2和__attribute __((弱))

时间:2017-04-03 05:41:02

标签: c gcc weak-linking

看起来像__attribute__((weak))#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" ); } } 的GCC会产生不同的结果,具体取决于您引用弱符号的方式。考虑一下:

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

$ cat test.c

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

$ make

1
1
1

$ ./test

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

$ make ADD_FLAGS =“ - O2”

0
1
1

$ ./test

use strict;
use warnings;

use Data::Dumper;

my @array;
my %hash;

$hash{'key1'} = 1;
$hash{'key2'} = 2;
$hash{'key3'} = 3;
$hash{'key4'} = 4;
push @array, {%hash};

$hash{'key1'} = 10;
$hash{'key2'} = 20;
$hash{'key3'} = 30;
$hash{'key4'} = 40;
push @array, {%hash};

# Expect:
#   @array[0] like 1,2,3,4
#   @array[1] like 10,20,30,40
print Dumper(@array); 

doChange(\@array[1]); # <<==== THIS IS THE BIT WHERE I NEED ASSISTANCE TO PASS ARRAY ELEMENT BY REFERENCE, IF POSSIBLE

# Expect:
#   @array[0] like 1,2,3,4
#   @array[1] like 100,200,300,400 <<== VALUES HAVE CHANGED
print Dumper(@array);      

sub doChange
{
    my %h = @_; # <<===  THIS IS NOT RIGHT EITHER!?

    $h{'key1'} = 100;
    $h{'key2'} = 200;
    $h{'key3'} = 300;
    $h{'key4'} = 400;
}

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

gcc版本5.4.0(GCC)

2 个答案:

答案 0 :(得分:2)

在进行优化时,编译器在声明const且在同一编译单元中具有weak定义的符号时遇到问题。

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

weak_def.c

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

此问题中描述的相同问题:GCC weak attribute on constant variables

答案 1 :(得分:1)

要点:

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

如果您尝试将弱符号初始化为任何值,即使为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语言没有“weak symbol”的概念。它是由ELF文件格式提供的功能,以及使用ELF文件格式的(动态)链接器。

由于man 1 nm手册页在"V"部分描述,

  

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

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

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

为确保在不与C编译器行为相冲突的情况下工作,“弱”符号必须未初始化,以便C编译器不能对其值进行任何假设。相反,它会像往常一样生成获取符号地址的代码 - 这就是普通/弱符号查找魔法发生的地方。

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