Как сбросить указатель стека в функциях «noreturn» на Cortex-M? - PullRequest
2 голосов
/ 29 марта 2019

В попытке уменьшить вероятность переполнения стека я хотел бы сбросить указатель стека после ввода функции, которая никогда не вернется. В моем коде есть два случая, когда это происходит: main () и ISR shutdown_immediate (), который сохраняет данные на флэш-память и входит в глубокий сон. Я использую LTO для подгонки кода, поэтому main () оказывается довольно большой функцией, которая требует выделения части стека для локальных переменных.

Моей первой попыткой было использование __attribute__ ((noreturn)) в сочетании с вызовом __builtin_unreachable(), но это никак не изменило сгенерированную сборку. Затем я создал встроенную функцию сборки для сброса указателя стека на последний адрес SRAM, который вызывается первым в main () и обработчике shutdown_immediate ().

inline __attribute__((always_inline)) void NO_RETURN (void)
{
        extern const uint32_t __stack_top__; // Defined in .ld file
        asm volatile ("ldr r3, %[stack_top]\n"
                      "mov sp, r3\n"
                      : /* no outputs */
                      : [stack_top] "m" (__stack_top__)
                      : /* no clobbers */
        );
}

int main (void)
{
    NO_RETURN();

    /* rest of the code here... */
}

void shutdown_immediate (void)
{
    NO_RETURN();
}

Это генерирует, казалось бы, правильный код для ISR. Однако для main() mov sp, r3 происходит после того, как стек выделен для локальных переменных и т. Д. Это не удастся, как только главная ветвь начнет ветвиться.

Сгенерированный код сборки:

00007f60 <shutdown_immediate>:
    7f60:   b570        push    {r4, r5, r6, lr}
    7f62:   4b21        ldr r3, [pc, #132]  ; (7fe8 <shutdown_immediate+0x88>)
    7f64:   681b        ldr r3, [r3, #0]
    7f66:   469d        mov sp, r3
; ...
    7fe8:   00202000    eoreq   r2, r0, r0 ; last SRAM address


00001180 <main>:
    1180:   b5f0        push    {r4, r5, r6, r7, lr}
    1182:   4be7        ldr r3, [pc, #924]  ; (1520 <main+0x3a0>)
    1184:   b097        sub sp, #92 ; 0x5c ; This SUB must be _after_ 0x1188!
    1186:   681b        ldr r3, [r3, #0]
    1188:   469d        mov sp, r3
; ...
    1520:   00202000    eoreq   r2, r0, r0 ; Last SRAM address

У кого-нибудь есть хитрости, как это можно сделать правильно? Я всегда мог создать второй вариант функции NO_RETURN(), который принимает значение выделения стека в качестве аргумента, компилирует, разбирает, снова компилирует и вставляет необходимый sub sp, #nn после mov sp, r3, но это грязное решение.

Архитектура: Cortex-M0

Проверенные цепочки инструментов:

  • НКУ-рука-ни-EABI-6-2017-q2-обновление
  • НКА-рычажный ни-EABI-8-2018-q4-мажорный

1 Ответ

0 голосов
/ 12 апреля 2019

Наиболее надежным решением было бы внедрить main в сборке и перейти к пользовательскому main оттуда (переименован в случае необходимости) после сброса стека. Взломать логику компилятора было бы слишком хрупко в долгосрочной перспективе.

...