Конфликт типа раздела для идентично определенных переменных
Этот вопрос возник в контексте этого вопроса: Найти неисполненные строки кода C++
При поиске этой проблемы большинство людей пытались добавить код и переменные в один и тот же раздел - но это определенно не проблема. Вот минимальный рабочий пример:
unsigned cover() { return 0; }
#define COV() do { static unsigned cov[2] __attribute__((section("cov"))) = { __LINE__, cover() }; } while(0)
inline void foo() {
COV();
}
int main(int argc, char* argv[])
{
COV();
if (argc > 1)
COV();
if (argc > 2)
foo();
return 0;
}
что приводит к g++ -std=c++11 test.cpp
(g++ (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)) в следующей ошибке:
test.cpp:6:23: error: cov causes a section type conflict with cov
COV();
^
test.cpp:11:30: note: ‘cov’ was declared here
COV();
^
Ошибка не очень полезна, так как не указывает, почему это конфликт. Как временные файлы.ii, так и.s не дают подсказки о том, в чем может быть проблема. На самом деле в файле.s есть только одно определение раздела
.section cov,"aw",@progbits
и я не понимаю, почему следующее определение должно конфликтовать с этим ("aw",@progbits правильно...).
Есть ли способ получить больше информации об этом? Видите, что это за точный конфликт? Или это просто ошибка...?
1 ответ
Сообщение действительно очень плохое, но это не ошибка. Проблема здесь возникает со встроенной функцией foo() и происходит потому, что встроенные функции должны быть определены в каждом контексте перевода, где они используются. В этой ссылке мы можем прочитать об атрибуте раздела:
"..uninitialized переменные ориентировочно попадают в раздел common (или bss) и могут быть многократно" определены ". Использование атрибута section изменяет раздел, в который входит переменная, и может привести к тому, что компоновщик выдаст ошибку, если неинициализированная переменная имеет несколько определений... "
Таким образом, когда функция foo должна быть "определена" в функции main, компоновщик находит переменную cov, ранее определенную во встроенной функции foo, и выдает ошибку.
Давайте сделаем работу препроцессора и расширим определение COV(), чтобы помочь прояснить проблему:
inline void foo()
{
do { static unsigned cov[2] __attribute__((section("cov"))) = { 40, cover() }; } while(0);
}
int main(int argc, char *argv[]) {
do { static unsigned cov[2] __attribute__((section("cov"))) = { 44, cover() }; } while(0);
if (argc > 1)
do { static unsigned cov[2] __attribute__((section("cov"))) = { 47, cover() }; } while(0);
if (argc > 2)
foo();
Чтобы облегчить рассуждение, давайте изменим атрибут section определения в встроенной функции foo на cov.2 просто для компиляции кода. Теперь у нас нет ошибки, поэтому мы можем проверить объект (.o) с помощью objdump:
objdump -C -t -j cov ./cmake-build-debug/CMakeFiles/stkovf.dir/main.cpp.o
./cmake-build-debug/CMakeFiles/stkovf.dir/main.cpp.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l d cov 0000000000000000 cov
0000000000000000 l O cov 0000000000000008 main::cov
0000000000000008 l O cov 0000000000000008 main::cov
objdump -C -t -j cov.2 ./cmake-build-debug/CMakeFiles/stkovf.dir/main.cpp.o
./cmake-build-debug/CMakeFiles/stkovf.dir/main.cpp.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l d cov.2 0000000000000000 cov.2
0000000000000000 u O cov.2 0000000000000008 foo()::cov
Мы видим, что компилятор делает foo:: cov, в разделе cov.2 GLOBAL (подписано буквой 'u'). Когда мы используем одно и то же имя раздела (cov), компилятор, пытающийся "определить" foo в главном блоке, обнаруживает предыдущий глобально определенный cov и выдает ошибку.
Если вы делаете встроенный foo статическим (inline static void foo()
,,.), который не позволяет компилятору выдавать код для встроенной функции и просто копирует его во время раскрытия, вы увидите, что ошибка исчезает, потому что нет глобального foo::cov.