Формирование определенных переменных, сгенерированных компилятором, в определенные разделы ELF (с помощью gcc) - PullRequest
11 голосов
/ 23 июня 2011

Я начну с окончательного вопроса: в 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__*) где-то еще, но мне нужно было бы изменить исходный глобус, чтобы исключить его, чтобы я не получил две копии.

Ответы [ 2 ]

3 голосов
/ 23 июня 2011

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

gcc -fdata-sections -S -o test.s test.c
sed 's/^\t.section\t\.rodata\.__func__\.[0-9]*/\t.section .rom_data/' -i test.s
gcc -c test.s

Вы также можете попробовать написать проход преобразования clang для размещения объявлений __func__ в выбранном вами разделе или написать программу для работы с объектными файлами, используя libbfd .

2 голосов
/ 25 июня 2011

Похоже, я в конце концов ответил на свой собственный вопрос о бизнесе -fdata-sections, я просто недостаточно разбирался в компоновщике GNU, чтобы увидеть его. На самом деле я не нуждаюсь в глобализации с исключением, если я сначала указываю бит *(.rodata.__func__*). Любые разделы, которые совпадают, будут помечены как использованные, поэтому более поздний глоб для *(.rodata*) не будет считать их дважды и скопировать в другое место. Мне не нужно помечать их ROM_STR. Круто!

Важно отметить, что -fdata-sections на самом деле помещает каждую строку функции в отдельный раздел .rodata.__func__.1234 (я не уверен, по какому шаблону следуют числа). Я не знаю, получают ли анонимные строки свои собственные разделы; в таком случае я мог бы использовать те же приемы компоновщика для захвата всех анонимных строк вместо макроса атрибута раздела ROM_STR, но это, вероятно, было бы плохой идеей. ROM_STR используется в макросе LOG, поэтому он гарантированно будет применяться только к строкам формата журнала. Если бы я помещал все анонимные строки в ПЗУ с помощью трюка с компоновщиком, это включало бы обычные данные сообщения, и я бы заплатил штраф за производительность во время выполнения для доступа к нему из флэш-памяти. Поэтому я не знаю, возможно ли это вообще, но его целесообразность будет зависеть от ваших конкретных системных требований.

...