GCC-O2 и атрибут ((слабый))
Похоже, что GCC с -O2
и __attribute__((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( "0n" );
}
else
{
printf( "1n" );
}
printf( "%dn", weaksym2 );
if ( weaksym3 == 0 )
{
printf( "0n" );
}
else
{
printf( "1n" );
}
}
$ кошачий тест.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
Вопрос в том, почему последний"./ тест " производит "0 1 1", а не"1 1 1"?
GCC version 5.4.0 (GCC)
2 ответа:
Похоже, что при выполнении оптимизации компилятор испытывает проблемы с символами, объявленными
const
и имеющими определениеweak
в одном и том же блоке компиляции.Вы можете создать отдельный файл c и переместить туда определения const weak, это позволит обойти проблему:
Weak_def.c
const int weaksym1 __attribute__(( weak )) = 0; const int weaksym2 __attribute__(( weak )) = 0;
Та же проблема, описанная в этом вопросе: слабый атрибут GCC на постоянных переменных
Резюме:
Слабые символы работают корректно только в том случае, если вы не инициализируете их значением. Компоновщик заботится об инициализации (и он всегда инициализирует их до нуля, если нет нормального символа с тем же именем).
Если вы попытаетесь инициализировать слабый символ до любого значения, даже до нуля, как это сделал 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__));
Подробное описание:
Язык Си не имеет понятия"слабый символ ". Это функциональные возможности, предоставляемые файлы в формате Elf, и (динамическая) линкеры, которые используют файлы в формате ELF.В качестве
man 1 nm
man page описывает в разделе"V"
,Когда слабый определенный символ связан с нормальным определенным символом, то нормальным определяемым символом является используется без ошибок. Когда слабый неопределенный символ связан и символ не определен, значение слабого символа становится нулем без ошибки.
Объявление слабого символа не должно быть инициализировано каким-либо значением, поскольку оно будет иметь нулевое значение, если процесс не связан с обычным символом того же имени. ( "определенный" на странице
man 1 nm
обозначает символ, существующий в таблице символов ELF.)Функция "слабый символ" была разработана для работы с существующие компиляторы Си. Помните, что компиляторы C не имеют никакого различия между" слабыми "и" нормальными " символами.
Чтобы гарантировать, что это будет работать без нарушения поведения компилятора C, "слабый" символ должен быть неинициализирован, так что компилятор C не может делать никаких предположений относительно его значения. Вместо этого он будет генерировать код, который получает адрес символа как обычно - и именно там происходит магия поиска нормального/слабого символа.
Это также означает, что слабые символы может быть только "автоинициализировано" до нуля, а не любое другое значение, если только оно не" переопределено " обычным инициализированным символом с тем же именем.