Я начну с окончательного вопроса: в C с gcc можно получить значения (s) __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
.
Но скажу, что я хочу, чтобы они поехали в другое место (я на встроенной платформе, где почти все работает из ОЗУ для скорости, но ограничения памяти нажимают для перемещения некоторых вещей в ПЗУ). "Легко" перемещать __FILE__
и строку формата:
#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";
Появилсягде function-name - имя лексически-охватывающей функции. Это имя является неотъемлемым именем функции.... Эти идентификаторы не являются макросами препроцессора. В GCC 3.3 и ранее, и только в C,
__FUNCTION__
и__PRETTY_FUNCTION__
рассматривались как строковые литералы; они могут использоваться для инициализации массивов char, и они могут быть объединены с другими строковыми литералами. GCC 3.4 и более поздние рассматриваются как переменные, например__func__
.
Таким образом, если вы завернете __func__
с помощью ROM_STR
, вы получите
error: invalid initializer
и если вы попытаетесь поместить атрибут раздела до или после использования __func__
, вы получите
error: expected expression before ‘__attribute__’
или
error: expected ‘)’ before ‘__attribute__’
И, таким образом, мы возвращаемся к первому вопросу: возможно ли получить __func__
в разделе по моему выбору? Может быть, я могу использовать -fdata-sections
и сделать магию компоновщика script, чтобы получить .rodata.__func__.*
исключение из остальной части .rodata
? Если да, то какой синтаксис для globbing с исключением в компоновщик script? Другими словами, где-то у вас есть *(.rodata*)
- я мог бы поставить *(.rodata.__func__*)
где-то в другом месте, но мне нужно будет изменить исходный глобус, чтобы исключить его, поэтому я не получаю две копии.