Невыровненный доступ вызывает ошибку на ARM Cortex-M4

У меня есть объект с адресом, не выровненным по 4 байтам. Это вызывает ошибку HardFault в процессоре, когда есть инструкция STR, сохраняющая 2 регистра.

Это сгенерированный код:

   00000000 <_ZN8BaseAreaC1EPcmm>:
   0:   b510            push    {r4, lr}
   2:   4604            mov     r4, r0
   4:   6042            str     r2, [r0, #4]
   6:   e9c4 3102       strd    r3, r1, [r4, #8]
   a:   2001            movs    r0, #1
   c:   7420            strb    r0, [r4, #16]
   e:   b921            cbnz    r1, 1a <_ZN8BaseAreaC1EPcmm+0x1a>

Это регистры, когда в строке "4: 6042..."

R0   08738B82  R8          0  
R1   08738BAE  R9          0  
R2          0  R10  082723E0  
R3       2FCC  R11         0  
R4   08738B82  R12         0  
R5   20007630  R13  2000CB38  

Как видно целевой регистр для STR-инструкций не выровнен по 4 байта. Инструкция STR r2, [r0, #4] выполняется нормально. Но это HardFaults на следующем STRD r3, r1, [r4, #8]. Если я вручную изменю регистр R4 на 08738B80, это не приведет к жесткому сбою.

Это код C++, который генерирует приведенный выше asm:

BaseArea::BaseArea(char * const pAddress, unsigned long startOffset, unsigned long endOffset) : 
m_pAddress(pAddress), m_start(startOffset), m_end(endOffset), m_eAreaType(BASE_AREA) {

И m_start является первой переменной в классе и имеет тот же адрес, что и this (08738B82), m_end следует после 0x08738B86.

Как мне выровнять объект по 4 байтам? У кого-нибудь есть другое решение для этого?


person rdrmntn    schedule 16.08.2013    source источник
comment
Вы на самом деле программируете на ассемблере или этот код генерируется, например, компилятор Си?   -  person Some programmer dude    schedule 16.08.2013
comment
@JoachimPileborg: Учитывая, что имена C++ явно искажены, я подозреваю, что это код C++.   -  person Mats Petersson    schedule 16.08.2013
comment
Это ассемблерный код, сгенерированный из C++.   -  person rdrmntn    schedule 16.08.2013
comment
@jortan: Что такое class или struct, которое вы здесь используете (значение в r0, вероятно, равно this объекта, с которым мы имеем дело. Другими словами, какой исходный код использовался для его создания?   -  person Mats Petersson    schedule 16.08.2013
comment
Также может помочь, если вы сообщите нам, какой компилятор вы используете (например, gcc, armcc и т. д.).   -  person Mats Petersson    schedule 16.08.2013
comment
@MatsPetersson обновил сообщение с кодом C++.   -  person rdrmntn    schedule 16.08.2013
comment
Не могли бы вы опубликовать структуру BaseArea, а также где вызывается конструктор?   -  person Mats Petersson    schedule 16.08.2013
comment
Как вы создаете экземпляр объекта? Может быть, это часть упакованной структуры или что-то в этом роде? В противном случае я думаю, что это регистрируется как ошибка цепочки инструментов, потому что язык гарантирует, что объекты будут размещены по адресам, которые соответствуют требованиям их выравнивания (за исключением объектов с чрезмерным выравниванием, но здесь это не так). В любом случае вы можете принудительно выполнить определенное выравнивание, используя alignas (C++11) или специальный эквивалент компилятора при создании объекта.   -  person Samuel Peter    schedule 13.02.2018


Ответы (2)


В системах на базе ARM вы часто не можете адресовать 32-битное слово, которое не выровнено по 4-байтовой границе (как говорит вам ваша ошибка). В x86 вы можете получить доступ к невыровненным данным, однако производительность сильно снижается. Если часть ARM поддерживает невыровненный доступ (например, нормальная загрузка одного слова), это снижает производительность и должна быть настраиваемая ловушка исключения.

Пример ошибки границы в ARM (здесь), TLDR: сохранение указателя на unsigned char а затем пытается преобразовать его в double * (двойной указатель).

Чтобы решить вашу проблему, вам нужно будет запросить блок памяти, выровненный по 4 байтам, и скопировать невыровненные байты + заполнить его мусорными байтами, чтобы убедиться, что он выровнен по 4 байтам (следовательно, выполните выравнивание структуры данных вручную). Затем вы можете интерпретировать этот объект как 4-байтовый, выровненный по новому адресу.

От TurboJ в комментариях явная ошибка:

Cortex-M3 и M4 по умолчанию разрешают невыровненный доступ. Но они не разрешают одновременный доступ с инструкцией STRD, отсюда и ошибка.

Вам также может быть полезно изучить это для принудительной структуры данных выравнивание на ARM.

person Community    schedule 16.08.2013
comment
на x86 снижение производительности происходит только тогда, когда ваш доступ пересекает границу 64-битной строки (или, не дай Бог, границу страницы). Простой доступ к паре невыровненных байтов внутри строки не имеет значения, вы просто кэшируете всю строку. - person Leeor; 16.08.2013
comment
@Leeor, я поставил свой первоначальный ответ как вики сообщества, поскольку ARM - это не та область, с которой я слишком хорошо знаком (позволяя другим людям вносить свой вклад в один коллективный, сильный ответ). Есть довольно много деталей, которые, я уверен, я упускаю из виду или не указываю, не стесняйтесь редактировать исходный пост соответственно. - person Jacob Pollack; 16.08.2013
comment
Извините, я, наверное, еще меньше знаком с микроархитектурой ARM, я говорил об эталоне x86. - person Leeor; 16.08.2013
comment
Спасибо! Проблема заключалась в том, что невыровненный адрес возник в результате ряда вычислений, включающих sizeof(), и адрес был выровнен по 2 байтам. Я изменю эту часть кода, чтобы выровнять ее по 4 байтам. - person rdrmntn; 16.08.2013
comment
Мне все еще любопытно, почему первый STR с одним регистром работает, а STR с двумя регистрами нет. Любые идеи по этому поводу? Существуют ли какие-либо параметры компилятора для этих проблем с выравниванием? - person rdrmntn; 16.08.2013
comment
@Leeor: OP заявил, что это код, сгенерированный компилятором C++, поэтому я не уверен, насколько актуальна последняя ссылка в вашем ответе. - person idoby; 16.08.2013
comment
Cortex-M3 и M4 по умолчанию разрешают невыровненный доступ. Но они не разрешают одновременный доступ с помощью инструкции STRD, отсюда и ошибка. - person Turbo J; 16.08.2013
comment
ARM не микрокодированы, x86 был и/или есть. arm и x86 будут иметь одинаковые/похожие наказания за невыровненный доступ, в обоих случаях это зависит от ширины шины относительно того, должен ли он выполнять один или два полных цикла. Кэш - это не проблема с падением производительности, кэшировано оно или нет, ARM или x86, снижение производительности происходит, если требуются две передачи, а не одна. Кэш может просто умножить наказание на некоторую сумму. - person old_timer; 16.08.2013
comment
@dwelch - наказание заключается не только в другом доступе, в некоторых случаях разделение усложняет некоторые внутренние проверки (когерентность, пересылка, блокировка и т. д.), поэтому неисправный ЦП (скажем, i7) может оштрафуйте эти нагрузки, которые будут выполняться по порядку. Это серьезная проблема, поскольку он сериализует параллельные операции. Arm-M3/4 не будет иметь этой конкретной проблемы (по сравнению с правильно выровненными доступами) - person Leeor; 21.08.2013
comment
есть штрафы, некоторые хуже, чем другие, в зависимости от архитектуры/платформы и ситуации во время инструкции/передачи. - person old_timer; 21.08.2013

Следующее верно, по крайней мере, для архитектуры ARM (проверено на коре головного мозга M0):

При использовании инструкций загрузки и сохранения память, к которой мы обращаемся, должна быть кратна количеству байтов, к которым мы пытаемся получить доступ из/в память, иначе мы получим исключение жесткой ошибки.

eg:

LDR r0, = 0x1001
LDR r1, [r0]

Вторая строка в приведенном выше коде выдаст серьезную ошибку, поскольку мы пытаемся прочитать 4 байта, но адрес памяти не делится на 4.

Если мы изменим вторую строку в приведенном выше коде на следующую

LDRB r1, [r0];//Загрузить 1 байт из адреса

Приведенная выше строка не вызовет серьезной ошибки, так как мы пытаемся получить доступ к 1 байту (доступ к 1 байту можно получить из любой области памяти).

Также обратите внимание на следующий пример;

LDR r0,= 0x1002
LDRH r1,[r0];   //Load half word from 0x1002

Приведенная выше строка не вызовет серьезной ошибки, поскольку доступ к памяти составляет 2 байта, а адрес делится на 2.

person Gaurav Rathi    schedule 26.08.2014
comment
Похоже, что для Cortex-M3, по крайней мере, не выровненный по словам адрес с инструкциями LDR и STR поддерживает невыровненный доступ и генерирует ошибки выравнивания только тогда, когда бит UNALIGN_TRP равен «1» в регистре управления конфигурацией. - person Uchia Itachi; 25.07.2015