Программирование ARM: решение проблем, FAQ IAR Warning[Pa039]: use of address of unaligned structure member Tue, November 12 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


IAR Warning[Pa039]: use of address of unaligned structure member Печать
Добавил(а) microsin   

Такое сообщение компилятор IAR 4.41A (программа микроконтроллера ARM7 в режиме Thumb) выдавал на код модуля ff.c библиотеки FatFs-07c [1]. Пример строки, на которую выдается предупреждение Pa039 (параметр, который вызывает предупреждение, выделен в примере жирным шрифтом):

res = auto_mount(&path, &dj.fs, 0);

Здесь компилятор предупреждает о том, что используется адрес не выровненного на 4 байта члена структуры. В данном случае это структура DIR dj, в которой отключено выравнивание полей директивой #pragma pack(1):

/* Объект структуры каталога */
//typedef __packed struct _DIR_ 
#pragma pack(1)
typedef struct _DIR_ 
{
   FATFS*   fs;      /* Указатель на владельца объекта файловой системы */
   WORD     id;      /* Идентификатор монтирования владельца файловой системы (mount ID) */
   WORD     index;   /* Индекс текущей операции чтения/записи */
   DWORD    sclust;  /* Начало таблицы кластеров (0: статическая таблица) */
   DWORD    clust;   /* Текущий кластер */
   DWORD    sect;    /* Текущий сектор */
   BYTE*    dir;     /* Указатель на текущую запись SFN (короткое имя файла) в win[] */
   BYTE*    fn;      /* Указатель на SFN (in/out) {file[8],ext[3],status[1]} */
#if _USE_LFN
   WCHAR*   lfn;     /* Указатель на рабочий буфер LFN (длинное имя файла) */
   WORD     lfn_idx; /* Последний подошедший индекс LFN (0xFFFF: нет LFN) */
#endif
} DIR;
#pragma pack()

Какие может иметь последствия предупреждение Pa039, и как от него избавиться? Обычно на 32-разрядных процессорах ARM7 это означает только то, что для не выровненных структур может использоваться не такой эффективный код, какой мог бы быть сгенерирован для структур, у которых все поля выровнены на адрес, нацело делящийся на 4. Для конкретного случая с FatFS можно совсем убрать директиву выравнивания #pack(1), что никак не отразится на работоспособности кода.

[Что такое выравнивание структуры?]

Все современные CPU ожидают обработки фундаментальных типов, таких как int, long и float, которые сохранены в памяти по адресам, кратным длине этих фундаментальных типов (так называемые выровненные адреса). CPU оптимизированы для доступа к выравненным адресам адресного пространства. Некоторые CPU (такие как Intel x86) допускают невыровненный доступ к памяти, но ценой снижения быстродействия. Некоторые CPU перехватывают невыровненный доступ как исключение (прерывание ошибки, которое обычно передается операционной системе), чтобы такие события могли быть игнорированы, симулированы выполнением специальных подпрограмм или о них передавались сообщения об ошибке. Другие CPU (как ранние процессоры ARM) использовали невыровненный адрес, что означало выполнение специальных операций при загрузке данных их памяти (load) или сохранении данных в память (store).

Когда компилятор C обрабатывает декларацию структуры, он может добавить дополнительные байты между полями структуры, чтобы гарантировать, что начальные адреса всех полей получили правильно выровненные адреса в памяти. Это также гарантирует, что все экземпляры этой структуры, когда они определяются в программе, будут находиться в памяти по выровненным адресам. Это также добавит дополнительные байты в конце структуры, чтобы гарантировать, что все массивы, входящие в структуру, были правильно выравнены. Функция "malloc" и тому подобные библиотечные функции для динамического выделения памяти из кучи всегда возвратят указатели на память, значение адреса в которых выровнены к критичному фундаментальному типу процессора (например, для ARM7 Atmel фундаментальный тип это 32 битное слово, тип int или unsigned int).

Спецификации для языка C и C++ устанавливают, что само существование и природа таких выравнивающих дополнительных байт полностью "implementation defined", т. е. определено реализацией компилятора и платформы, на которой выполняется код. Это означает, что каждая комбинация процессор + операционная система + компилятор свободны в использовании любого выравнивания, которое лучше всего подходит для наиболее эффективного целевого кода. Предполагается, что программисты (в обычных случаях) не учитывают определенные правила для дополнения данных пустыми байтами и выравнивания адресов. Таким образом, в сами языки не предусматривают индикацию специальной поддержки выравнивания и добавления пустых байт, хотя многие компиляторы (включая gcc и IAR) имеют нестандартные расширения, чтобы позволить управление выравниванием (в IAR версии 4 это директива #pragma pack, в более поздних версиях IAR это атрибут __packed, подробнее см. [2]).

Таким образом "выравнивание структуры" это выбор правил, задающих, где и когда добавляются пустые байты (padding), чтобы мог быть сгенерирован более эффективный код для процессора.

[Почему неправильное выравнивание может вызвать проблемы?]

Ранние процессоры ARM имели очень ограниченные возможности доступа к ячейкам памяти, которые расположены по не выровненному на 4 байта адресу (т. е. когда адрес переменной не делится нацело на 4). Более современные процессоры ARM (в противоположность StrongArm) имеют урезанную поддержку доступа к половинкам слова (т. е. к 16-разрядным значениям short int). Первые компиляторы разрабатывались для использования кода в приложениях встраиваемых систем. Разработчики компилятора выбирали разрешить декларировать типы короче, чем слово 32 бита (типы char и short), но выравнивали при этом все структуры по границе 32-битного слова, чтобы увеличить быстродействие при доступе ко всем переменным программы.

Эти правила отлично соответствуют спецификациям языков C and C++, но они отличаются от правил, виртуально используемых для всех других 32-разрядных и 64-разрядных микропроцессоров. Операционная система Linux и её приложения ранее редко портировались на платформы с особыми правилами выравнивания, так что в коде могут быть скрытые дефекты, связанные с неучтенными правилами выравнивания определенных типов данных. Эти дефекты проявились, когда приложения начали портировать на платформу ARM.

Эти скрытые дефекты могут привести с снижению быстродействия, повреждению данных и к сбою программы. Конечный эффект зависит от того, как сконфигурированы компилятор и операционная система, а также от природы дефектного кода. Есть 3 способа исправить такие скрытые дефекты:

1. Изменить для компилятора правила выравнивания, чтобы они соответствовали используемому приложению (или другим операционным системам Linux).
2. Использовать перехват ошибки выравнивания (обработку прерывания исключения), чтобы исправить некорректные обращения к памяти.
3. Найти и исправить все скрытые дефекты, каждый по отдельности.

Эти 3 альтернативы в некоторой степени исключают друг друга. У каждой есть определенные достоинства и недостатки. Все могут быть успешно применены, но "правильное" решение, конечно, зависит от Ваших предпочтений и конкретной задачи.

[Ссылки]

1Библиотека FatFS: модуль файловой системы FAT.
2. IAR EW ARM: выравнивание полей в структурах.

 

Добавить комментарий


Защитный код
Обновить

Top of Page