Сохраняются ли регистры стека ARM Cortex-M0 на $ psp или $ msp во время Hardfault? - PullRequest
0 голосов
/ 30 августа 2018

У меня проблема с ошибкой моего Cortex-M0, поэтому я пытаюсь отладить его. Я пытаюсь распечатать содержимое регистров ядра ARM, которые были помещены в стек при возникновении сбоя.

Вот мой основной код сборки:

__attribute__((naked)) void HardFaultVector(void) {
    asm volatile(
        // check LR to see whether the process stack or the main stack was being used at time of exception.
        "mov r2, lr\n"
        "mov r3, #0x4\n"
        "tst r2, r3\n"
        "beq _MSP\n"

        //process stack was being used.
        "_PSP:\n"
        "mrs r0, psp\n"
        "b _END\n"

        //main stack was being used.
        "_MSP:\n"
        "mrs r0, msp\n"
        "b _END\n"

        "_END:\n"
        "b fault_handler\n"
    );  
}  

Функция fault_handler напечатает содержимое кадра стека, который был помещен либо в стек процессов, либо в основной стек. Вот мой вопрос, хотя:

Когда я печатаю содержимое кадра стека, который предположительно имеет сохраненные регистры, вот что я вижу:

Stack frame at 0x20000120:
 pc = 0xfffffffd; saved pc 0x55555554
 called by frame at 0x20000120, caller of frame at 0x20000100
 Arglist at unknown address.
 Locals at unknown address, Previous frame's sp is 0x20000120
 Saved registers:
  r0 at 0x20000100, r1 at 0x20000104, r2 at 0x20000108, r3 at 0x2000010c, r12 at 0x20000110, lr at 0x20000114, pc at 0x20000118, xPSR at 0x2000011c

Вы можете видеть сохраненные регистры, это регистры, которые выдвигаются ядром ARM при возникновении серьезного сбоя. Вы также можете увидеть строку pc = 0xfffffffd;, которая указывает, что это значение LR EXC_RETURN. Значение 0xfffffffd указывает мне, что стек процессов использовался во время серьезной ошибки.

Если я напечатаю значение $psp, я получу следующее:

gdb $ p/x $psp
$91 = 0x20000328

Если я напечатаю значение $msp, я получу следующее:

gdb $ p/x $msp
$92 = 0x20000100

Вы можете ясно видеть, что $msp указывает на вершину стека, где предположительно находятся сохраненные регистры. Не означает ли это, что в главном стеке есть сохраненные регистры, которые ядро ​​ARM поместило в стек?

Если я печатаю содержимое памяти, начиная с адреса $msp, я получаю следующее:

gdb $ x/8xw 0x20000100
0x20000100 <__process_stack_base__>:    0x55555555  0x55555555  0x55555555  0x55555555
0x20000110 <__process_stack_base__+16>: 0x55555555  0x55555555  0x55555555  0x55555555

Пусто ...

Теперь, если я распечатываю содержимое памяти, начиная с адреса $psp, я получаю следующее:

gdb $ x/8xw 0x20000328
0x20000328 <__process_stack_base__+552>:    0x20000860  0x00000054  0x00000054  0x20000408
0x20000338 <__process_stack_base__+568>:    0x20000828  0x08001615  0x1ad10800  0x20000000

Это выглядит более точно. Но я думал, что сохраненные регистры должны указывать, где они находятся во флэш-памяти? Так как это имеет смысл?

1 Ответ

0 голосов
/ 30 августа 2018

Комментарии old_timer по вашему вопросу верны. Регистры будут помещены в активный стек при входе исключения, будь то PSP или MSP в то время. По умолчанию весь код использует основной стек (MSP), но если вы используете что-то, кроме чистого железа, вполне вероятно, что любое используемое ядро ​​переключило режим Thread на использование стека процессов (PSP).

Большинство ваших исследований показывают, что PSP использовался, и ваш взгляд на PSP и MSP был довольно бесспорным. Единственным подтверждением того, что вы были MSP, являются результаты функции fault_handler, для которой вы не опубликовали источник; поэтому мое первое предположение было бы, что эта функция каким-то образом нарушена.

Помните также, что одной из распространенных причин ввода обработчика HardFault является то, что другой обработчик исключений вызвал исключение. Это может легко произойти в случаях повреждения памяти. В этих случаях (при условии, что в режиме Thread используется PSP) ЦПУ сначала переходит в режим обработчика в ответ на исходное исключение, нажимая r0-r3,r12,lr,pc,psr в стек процесса. Он начнет выполнение исходного обработчика исключений, а затем снова выдаст ошибку, нажав r0-r3,r12,lr,pc,psr в основной стек при входе в обработчик HardFault. Часто нужно кое-что распутать.

old_timer также упоминается использование реального языка ассемблера, и я согласен и здесь. Даже если атрибут ((naked)) должен удалять пролог и эпилог (между ними большинство возможных «компилиризмов»), ваш код был бы гораздо более читабельным, если бы он был написан на голом ассемблере. Встроенный язык ассемблера имеет свои применения, например, если вы хотите сделать что-то очень низкоуровневое, чего вы не можете сделать из C, но вы хотите избежать издержек возврата вызова. Но когда вся ваша функция написана на ассемблере, нет смысла ее использовать.

...