Запретить gcc удалить неиспользуемую переменную

В наших исходных файлах обычно есть строка версии:

static const char srcvers[] = "VERSION/foo.c/1.01/09.04.15";

Когда эта строка не оптимизирована, она весьма полезна в некоторых случаях, так как можно определить версию каждого исходного файла, связанного с исполняемым файлом, просто вызывая strings a.out | grep VERSION.

К сожалению, оптимизирован gcc (используя '-O'). Поэтому мой вопрос заключается в том, есть ли простой способ (компилятор должен был бы отлично работать), чтобы gcc сохранил эту переменную (ее имя всегда одно и то же), не отключая других оптимизаций.

Edit

Что, на мой взгляд, отличает вопрос от от того, что я, это то, что я надеялся найти решение, для которого мне не пришлось бы коснитесь тысяч исходных файлов.

Ответ 1

Вы можете использовать __attribute__((used)) gcc (также работает в clang) (я вижу, что этот вопрос отмечен gcc) атрибутами для этого:

Этот атрибут, привязанный к функции, означает, что код должен быть испущен для функции, даже если кажется, что функция не ссылается. Это полезно, например, когда функция ссылается только на встроенную сборку.

От https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Демо:

$ cat a.c
static const char srcvers[] __attribute__((used)) = "VERSION/foo.c/1.01/09.04.15";
$ gcc -O3 -c a.c
$ strings a.o
VERSION/foo.c/1.01/09.04.15

Вы можете использовать некоторые #if и #define, чтобы сделать этот терьер, а также скомпилировать компиляторы, которые не поддерживают это расширение.

Ответ 2

Насколько я понимаю ваш вопрос, вам нужно добавить строку версии в каждый объектный файл, не касаясь источников. Это можно сделать следующим образом.

Создайте заголовочный файл, например include/version.h:

#ifndef VERSION_H
#define VERSION_H

static const char _ver[] __attribute__((used)) = "VERSION/foo.c/1.01/09.04.15";

#endif /* VERSION_H */

Затем в вашем Makefile (или независимо от вашей системы сборки) добавьте следующий флаг gcc:

CPPFLAGS += -include include/version.h

Конечно, он должен быть передан в gcc, например. например:

%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).c

Теперь вы можете наблюдать за строкой _ver, скомпилированной для каждого объектного файла:

$ objdump -DS src/main.o | grep _ver

Что будет показано вам примерно так:

Disassembly of section .rodata._ver:
00000000 <_ver>:

Ответ 3

Объявление переменной volatile также может помочь. Именно поэтому он используется в первую очередь, предотвращая какие-либо оптимизации компилятором относительно этой переменной.

Ответ 4

Похоже, что все решения требуют какого-то украшения строки версии в источнике, это может помочь определить макрос, содержащий весь необходимый синтаксис, а затем использовать этот макрос в файлах источника или заголовка при необходимости:

#define SRCVERSION(file, version, data) static const char _ver[] __attribute__((used)) = "VERSION/" file "/" version "/" date;

Затем в свой источник просто поставьте

SRCVERSION("foo.c", "1.01", "09.04.15")

Макрос может находиться в центральном файле заголовка проекта или в командной строке компилятора.

Таким образом, по крайней мере вам не придется прикоснуться ко всем исходным файлам снова, если вы хотите что-то изменить в определении.

Обратите внимание, как определение макроса использует конкатенацию строк для построения окончательной версии. Также он содержит конечную точку с запятой, поэтому вы можете удалить все, указав пустой макрос, если это необходимо.

Ответ 5

Вас беспокоит gcc удаление неиспользуемой переменной static char[]. AFAIK, компилятор прав для этого.

В других ответах было предложено улучшить это. Но вы не хотите изменять исходный код тысяч файлов.

Затем вы, возможно, можете изменить свою сборку (например, некоторые Makefile), чтобы каждый такой исходный файл, используя ваш трюк (что немного неправильно, как обсуждалось здесь...), не нужно было менять. Таким образом, вы можете специально указать GCC. Вы хотите

 static const char _ver[] __attribute__((used));

(это объявление, а не определение) для компиляции перед чем-либо еще. Поместите строку выше в некоторый файл _declare_ver.h и скомпилируйте с помощью команды gcc -include _declare_ver.h (вместо gcc). Если используется make добавить

 CFLAGS += -include _declare_ver.h

в Makefile.

Кстати, это грязный трюк. Вам следует подумать о том, чтобы сделать что-то лучше (после других ответов).