long_calls между разделами RAM и ROM на голой железной ARM с gcc - PullRequest
10 голосов
/ 18 июня 2011

Я работаю над проектом ARM7TDMI с использованием GCC 4.3, и у меня возникли сложности с указанием компилятору использовать длинные вызовы в некоторых случаях, но не в других.

Процесс сборки запускается arm-eabi-gcc для генерации перемещаемых объектных файлов ELF для каждого исходного файла .c (большинство соответствующих CFLAGS включают -Os -ffunction-sections -fdata-sections -mthumb -mthumb-interwork), затем связывает их все в исполняемый файл ELF (наиболее важные LDFLAGS включают -Wl,--gc-sections -Wl,-static -Wl,-n -nostdlib, и пользовательский скрипт компоновщика). Затем этот файл ELF преобразуется в необработанный исполняемый файл с arm-eabi-objcopy -O binary, и пользовательский загрузчик копирует его из ПЗУ в ОЗУ (одну SRAM с кодом и данными) при запуске. Таким образом, все затем выполняется из ОЗУ, .rodata присутствует в ОЗУ, и все происходит быстро, полностью игнорируя ПЗУ после загрузки.

Сейчас я пытаюсь это изменить, чтобы некоторые выбранные фрагменты данных RO и текст функций выбора могли жить только в ПЗУ и быть доступными во время выполнения по мере необходимости. Я изменил скрипт компоновщика, чтобы узнать о двух новых разделах ".flashdata" и ".flashtext", которые должны быть размещены по фиксированному адресу в ПЗУ. Я также разбросал __attribute__((__section__(".flashdata"))) и __attribute__((__section__(".flashtext"),__long_call__)) по всему коду C, в зависимости от ситуации, и перенастроил процесс сборки так, чтобы старая objcopy теперь добавила -R .flashdata -R .flashtext, и я делаю вторую objcopy с -j для каждого из этих разделов я объединяю два выходных файла, чтобы загрузчик мог делать правильные вещи, и разделы ПЗУ появлялись в ожидаемом месте отображения памяти.

Это все работает нормально - я могу распечатать строки, помеченные в разделе .flashdata, и я могу вызвать функцию .flashtext из кода, работающего вне ОЗУ (который знает, что использовать длинный вызов из-за атрибута __long_call__ рядом с атрибутом __section__(".flashtext")). Эта основанная на ПЗУ функция может с радостью вызывать другие функции на основе ПЗУ и возвращаться обратно к вызывающей программе на основе ОЗУ.

Проблема заключается в попытке вызвать функцию из ПЗУ в функцию, основанную на ОЗУ, что также должно быть длительным вызовом. Я не хочу использовать длинные вызовы везде, поэтому я не хочу -mlong_calls в моих CFLAGS. Если я сгруппирую все функции для жизни в ПЗУ в один rom.c, я могу создать этот один файл с -mlong-calls, и все будет работать. Тем не менее, я настоятельно предпочитаю избегать этого и сохранять функции, сгруппированные, как правило, по назначению, просто помечая некоторые из них здесь и там при необходимости для запуска из ПЗУ.

Кстати, этого было недостаточно при gcc 3.4. Использование -mlong-calls заставило компилятор думать правильно, но он не мог выполнить его, потому что он был готов выполнять длинные прыжки только со своими помощниками _call_via_rX ..., которые все жили в ОЗУ и могли быть доступны только через длинные вызов. Это было исправлено в компоновщике в gcc 4.0, но не перенесено ни на что в дереве 3.x .

Так что замечательно, что теперь я могу вообще перезванивать в ОЗУ, так как я использую gcc 4.3. Было бы еще лучше, если бы я мог как-то пометить код в функциях на основе ПЗУ, чтобы заставить его использовать длинные вызовы. Существует #pragma long_calls, но он влияет только на объявления, поэтому я мог бы использовать его вместо __attribute__((__long_call__)). К сожалению, он волшебным образом не заставляет компилятор использовать длинные вызовы для всех вызовов функций, с которыми он сталкивался, пока он действует.

С организационной точки зрения просто неправильно делать группу медленно работающего кода в одном файле вне контекста и отдельно от другого кода в его общей категории. Пожалуйста, скажите мне, что есть вариант, который я еще не рассматривал. Почему -ffunction-section или просто тот факт, что код находится в разных разделах (.text против .flashtext), автоматически не решают мою проблему?

Кстати, ошибка компоновщика, когда он обнаруживает, что компилятор использовал короткий вызов, который не оставил ему достаточно места для управления перемещением, это: relocation truncated to fit: R_ARM_THM_CALL against symbol foo ', определенный в разделе .text.foo в objs / foo.o (and the section .text.foo is used instead of .text because of -функция-секции` в CFLAGS).

Ответы [ 2 ]

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

Похоже, что проблема, вероятно, исправлена ​​в gcc 4.3.3 и более поздних версиях, но я использую 4.3.2.Я создал пример домашнего животного, который не будет выполнен, но демонстрирует использование различных разделов и полученную ошибку ссылки.Он не может быть собран с arm-2008q3 Codesourcery, но он собирается с arm-2009q1 и более поздними версиями.Мне потребуется больше времени, чтобы обновить весь проект, чтобы использовать более новую версию gcc, поэтому я пока не могу сказать однозначно, что это решает мою проблему, но я сильно подозреваю, что это так.

В целом,У меня есть другой обходной путь в качестве альтернативы группированию всех основанных на ПЗУ функций в -mthumb-calls -строенный rom.c: вызывать все через указатель на функцию.В случае этого обходного пути, лечение хуже, чем болезнь:

((void(*)(void*, void*, int))&memcpy+1)(&dest, &src, len);

Вам нужен +1, чтобы оптимизатор не перехитрил вас, но в моем случае это не проблема, потому что bx -ing по нечетному адресу указывает на режим большого пальца, и весь мой код - большой палец.Однако я не верю, что есть способ сделать универсальный макрос, чтобы обернуть все такие вызовы функций, так как каждый указатель должен быть явно приведен для соответствия типу возвращаемого значения и списку аргументов - вы фактически в конечном итоге явно декларируете каждую функциюВы хотите вызывать, даже библиотечные функции, которые вы не предоставляете.

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

Я подозреваю, что нет способа сделать то, что вы хотите автоматически.У меня есть одно предложение, хотя это не совсем то, что вы хотите.

Объявите функции RAM (скажем, в ram_funcs.h) следующим образом:

int foo() RAM_CALL;

Затем поместите все свои ПЗУфункционирует в свои собственные файлы и запускает каждый из этих файлов следующим образом:

#define RAM_CALL __attribute__((__long_call__))
#include "ram_funcs.h"

Другие файлы будут использовать:

#define RAM_CALL
#include "ram_funcs.h"

Это не намного лучше, чем использовать -mlong-calls, нопо крайней мере, это ставит логику рядом с самими функциями, а не в какой-то опции компиляции.Облегчает размещение, например, нескольких функций в каждом файле .c.

...