Я начну с главного вопроса: В 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
автоматически.
Но скажем, я хочу, чтобы они переместились в другое место (я использую встроенную платформу, на которой для скорости работает почти все из ОЗУ, но ограничения памяти подталкивают к перемещению некоторых вещей в ПЗУ). «Легко» переместить __ 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__)
Вы не можете поместить __ атрибут __
в анонимную строку, поэтому макрос 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-section
и сделать некоторую магию сценария компоновщика, чтобы исключить .rodata .__ func __. *
из остальной части .rodata
? Если да, то каков синтаксис подстановки с исключением в скрипте компоновщика? Другими словами, где-то у вас есть * (. Rodata *)
- я мог бы поместить * (. Rodata .__ func __ *)
где-нибудь еще, но мне нужно было бы изменить оригинал glob, чтобы исключить его, чтобы я не получил две копии.