Влияет ли на Link Link (LR) встроенные или скрытые функции?

Я использую процессор ARM Cortex-M4. Насколько я понимаю, LR (link link) хранит адрес возврата выполняемой в данный момент функции. Однако влияют ли на него встроенные и / или открытые функции?

Я работаю над реализацией простой многозадачности. Я хотел бы написать некоторый код, который сохраняет контекст выполнения (pusing R0R12 а также LR в стек), чтобы его можно было восстановить позже. После сохранения контекста у меня есть SVC поэтому ядро ​​может запланировать другую задачу. Когда он решит снова запланировать текущую задачу, он восстановит стек и выполнит BX LR, Я задаю этот вопрос, потому что я хотел бы BX LR прыгать в правильное место.

Допустим, я использую arm-none-eabi-g++ и я не связан с мобильностью.

Например, если у меня есть следующий код с always_inline атрибута, так как компилятор встроит его, то в результирующем машинном коде не будет вызова функции, поэтому LR не влияет, верно?

__attribute__((always_inline))
inline void Task::saveContext() {
asm volatile("PUSH {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR}");
}

Тогда есть также naked атрибут чья документация говорит, что это не будет иметь последовательностей пролога / эпилога, сгенерированных компилятором. Что именно это означает. Приводит ли голая функция к вызову функции и влияет ли она на LR?

__attribute__((naked))
void saveContext() {
asm volatile("PUSH {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR}");
}

Кроме того, из любопытства, что произойдет, если функция помечена как always_inline а также naked? Это имеет значение?

Какой правильный способ гарантировать, что вызов функции не влияет на LR?

0

Решение

Re: обновить. Оставляя мой старый ответ ниже, так как он отвечает на оригинальный вопрос до редактирования.

__attribute__((naked)) в основном существует, так что вы можете написать все функция в асме, внутри asm заявления вместо в отдельном .S файл. Компилятор даже не выдает инструкцию возврата, вы должны сделать это самостоятельно. Не имеет смысла использовать это для встроенных функций (как я уже ответил ниже).

Вызов naked функция будет генерировать обычную последовательность вызовов, с bl my_naked_functionчто, конечно, устанавливает LR для указания на инструкцию после bl, naked Функция по сути является никогда не встроенной функцией, которую вы пишете в asm. «пролог» и «эпилог» — это инструкции, которые сохраняют и восстанавливают регистры, сохраненные вызываемым пользователем, и сама инструкция возврата (bx lr).


Попробуйте и посмотрите. Это легко посмотреть на вывод asm gcc. Я изменил имена ваших функций, чтобы помочь объяснить, что происходит, и исправил синтаксис (GNU C __attribute__ продление требует удвоенных паренов).

extern void extfunc(void);

__attribute__((always_inline))
inline void break_the_stack() {   asm volatile("PUSH LR");   }

__attribute__((naked))
void myFunc() {
asm volatile("PUSH {r3, LR}\n\t"  // keep the stack aligned for our callee by pushing a dummy register along with LR
"bl extfunc\n\t""pop {r3, PC}");
}int foo_simple(void) {
extfunc();
return 0;
}

int foo_using_inline(void) {
break_the_stack();
extfunc();
return 0;
}

вывод asm с gcc 4.8.2 -O2 для ARM (по умолчанию, я думаю, цель большого пальца).

myFunc():            # I followed the compiler's foo_simple example for this
PUSH {r3, LR}
bl extfunc
pop {r3, PC}
foo_simple():
push    {r3, lr}
bl      extfunc()
movs    r0, #0
pop     {r3, pc}
foo_using_inline():
push    {r3, lr}
PUSH LR
bl      extfunc()
movs    r0, #0
pop     {r3, pc}

Дополнительный push LR означает, что мы вставляем неправильные данные в ПК. Может быть, еще одна копия LR, в этом случае, но мы возвращаемся с измененным указателем стека, поэтому вызывающая сторона прервется. Не связывайтесь с LR или стеком во встроенной функции, если только вы не пытаетесь сделать что-то вроде бинарного инструментария.


re: comments: если вы просто хотите установить переменную C = LR:

Как отмечает @Notlikethat, LR может не содержать обратный адрес. Так что вы можете захотеть __builtin_return_address(0) чтобы получить адрес возврата текущей функции. Однако, если вы просто пытаетесь сохранить состояние регистра, то вы должны сохранить / восстановить все, что имеет функция в LR, если вы хотите правильно возобновить выполнение в этот момент:

#define get_lr(lr_val)  asm ("mov %0, lr" : "=r" (lr_val))

Это может быть необходимо volatile чтобы он не поднимался вверх по дереву вызовов во время оптимизации всей программы.

Это приводит к дополнительной инструкции mov, когда, возможно, идеальной последовательностью будет сохранение lr, а не копирование сначала в другой регистр. Поскольку ARM использует разные инструкции для перемещения reg-reg и сохранения в памяти, вы не можете просто использовать rm ограничение для выходного операнда, чтобы дать компилятору эту опцию.

Вы можете обернуть это внутри встроенной функции. Оператор-выражение GNU C в макросе также будет работать, но встроенная функция должна подойти:

__attribute__((always_inline)) void* current_lr(void) {  // This should work correctly when inlined, or just use the macro
void* lr;
get_lr(lr);
return lr;
}

Для справки: Что такое SP (стек) и LR в ARM?


naked always_inline функция не полезна.

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

Если вы хотите добавить некоторые асмы, не используйте naked функция. Вместо этого используйте встроенную функцию, которая использует правильные ограничения для параметров ввода / вывода.

В вики есть несколько хороших встроенных ссылок asm, и они не все специфичны для x86. Например, см. сборник встроенных ссылок GNU в конце этого ответа примеры того, как правильно использовать синтаксис, чтобы компилятор создавал максимально эффективный код вокруг вашего фрагмента asm.

1

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

Насколько я понимаю, LR (link link) хранит адрес возврата выполняемой в данный момент функции.

Нету, lr просто получает адрес следующей инструкции при выполнении bl или же blx инструкция. В архитектуре M-класса он также получает специальное магическое значение при вводе исключения, которое вызывает возврат исключения, когда используется как адрес возврата, в результате чего обработчики исключений выглядят точно так же, как и обычные функции.

Когда функция введена, компилятор может сохранить это значение в другом месте и использовать r14 как просто еще один регистр общего назначения. Действительно, это потребности сохранить значение где-нибудь, если он хочет сделать любые вложенные вызовы. С большинством компиляторов любая неконечная функция будет lr в стек как часть пролога (и часто используют возможность вставлять его обратно в pc в эпилоге вернуться).

Какой правильный способ гарантировать, что вызов функции не влияет на LR?

Вызов функции по определению влияет lr — иначе это будет goto, а не колл (несмотря на фейл-коллы, конечно).

2

По вопросам рекламы [email protected]