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

4

Решение

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

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

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

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

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

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

12

Другие решения

Следующее верно для архитектуры 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.

0