Нераспределенный доступ вызывает ошибку на 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 байта? У кого-нибудь есть какое-то другое решение этого?

3 ответа

Решение

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

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

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

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

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

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

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

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

например:

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.

Как вы обнаружили, Cortex-M4 поддерживает 4-байтовый невыровненный доступ, но не 8-байтовый невыровненный доступ. Последнее объясняется в документации бита UFSR.UNALIGNED:

UNALIGNED — указывает, что произошла невыровненная операция доступа. Невыровненный доступ к множественным словам, например, доступ к не выровненному по 8 байтам, всегда будет генерировать эту ошибку. За исключением микроконтроллеров Cortex-M0, также можно настроить, будет ли невыровненный доступ ниже 4 байт генерировать ошибку.

8-байтовый доступ может быть инструкцией STR (как в вашем примере) или просто доступом кuint64_t.

Другие вопросы по тегам