У меня есть объект, адрес которого не выровнен по 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 байта?
У кого-нибудь есть какое-то другое решение этого?
В системах на основе 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.