Я начну с окончательного вопроса: в C с gcc возможно ли получить значение (я) __func__
(или эквивалентно, __FUNCTION__
), хранящееся в разделе, отличном от .rodata
(или где -mrodata=
баллов) или их подраздел?
Полное объяснение:
Скажем, у меня есть макрос регистрации:
#define LOG(fmt, ...) log_internal(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
(Оператор конкатенации строк ##
, используемый в этом унарном контексте, использует предыдущую запятую, если и только если список __VA_ARGS__
пуст, что позволяет использовать строку формата с аргументами или без них.)
Затем я могу использовать макрос нормально:
void my_function(void) {
LOG("foo!");
LOG("bar: %p", &bar);
}
может напечатать (очевидно, в зависимости от реализации log_internal
):
foo.c:201(my_function) foo!
foo.c:202(my_function) bar: 0x12345678
В этом случае строки формата ("foo"
и "bar: %p"
) и строки препроцессора ("foo.c"
и "my_function"
) являются анонимными данными только для чтения, и они автоматически помещаются в раздел .rodata
.
Но, скажем, я хочу, чтобы они отправились в другое место (я на встраиваемой платформе, использующей практически все из оперативной памяти для скорости, но ограничения памяти толкают для перемещения некоторых вещей в ПЗУ). «1030» и «строку формата» легко переместить:
#define ROM_STR(str) (__extension__({static const __attribute__((__section__(".rom_data"))) char __c[] = (str); (const char *)&__c;}))
#define LOG(fmt, ...) log_internal(ROM_STR(__FILE__), __LINE__, __func__, ROM_STR(fmt), ##__VA_ARGS__)
Вы не можете поместить __attribute__
в анонимную строку, поэтому макрос ROM_STR
дает ему временное имя, прикрепляет его к определенному разделу, а затем оценивает по начальному адресу, чтобы его можно было заменить без ошибок. Это не сработает, если вы попытаетесь передать переменную char *
в LOG
в качестве строки формата, но я хочу исключить этот вариант использования.
Обычно анонимные строки, которые оказываются идентичными, объединяются компилятором в одно место хранения, поэтому каждый экземпляр __FILE__
в одном файле будет иметь один и тот же адрес времени выполнения. С явным присвоением имени в ROM_STR
каждый экземпляр получит свое собственное место хранения, поэтому, вероятно, нет смысла использовать его в __FILE__
.
Однако я хотел бы использовать его на __func__
. Проблема в том, что __func__
не та же магия, что и __FILE__
. Из руководства gcc «Имена функций в виде строк»:
Идентификатор __func__
неявно объявляется переводчиком, как если бы, сразу после открывающей скобки каждого определения функции, объявление
static const char __func__[] = "function-name";
Появилось
, где имя-функции - это имя лексической функции. Это имя является неукрашенным именем функции.
...
Эти идентификаторы не являются макросами препроцессора. В GCC 3.3 и ранее, и только в C, __FUNCTION__
и __PRETTY_FUNCTION__
обрабатывались как строковые литералы; они могут быть использованы для инициализации массивов символов и могут быть объединены с другими строковыми литералами. GCC 3.4 и более поздние версии обрабатывают их как переменные, например __func__
.
Таким образом, если вы обернетесь __func__
с ROM_STR
, вы получите
error: invalid initializer
и если вы попытаетесь поместить атрибут раздела до или после использования __func__
, вы получите
error: expected expression before ‘__attribute__’
или
error: expected ‘)’ before ‘__attribute__’
И, таким образом, мы возвращаемся к первому вопросу: возможно ли хранить __func__
в выбранном мной разделе? Может быть, я могу использовать -fdata-sections
и использовать магию сценариев компоновщика, чтобы исключить .rodata.__func__.*
из остальной части .rodata
? Если да, то каков синтаксис для подстановки с исключением в сценарии компоновщика? Другими словами, где-то у вас есть *(.rodata*)
- я мог бы поставить *(.rodata.__func__*)
где-то еще, но мне нужно было бы изменить исходный глобус, чтобы исключить его, чтобы я не получил две копии.