Ответ 1
Некоторые соглашения о вызовах регистров зависят от ABI (двоичный интерфейс приложения). FP
требуется в стандарте APCS, а не в более новой AAPCS (2003). Для AAPCS (GCC 5. 0+) FP
не нужно использовать, но, безусловно, можно; информация об отладке аннотируется с помощью указателя стека и фрейма для отслеживания стека и разматывания кода с помощью AAPCS. Если функция - static
, компилятору действительно не нужно придерживаться каких-либо соглашений.
Как правило, все регистры ARM общего назначения. lr
(регистр связи, также R14) и pc
(программный счетчик также R15) являются специальными и закреплены в наборе команд. Вы правы, что lr
будет указывать на A. pc
и lr
связаны между собой. Один "где ты", а другой "где ты был". Они являются кодом аспекта функции.
Как правило, у нас есть sp
(указатель стека, R13) и fp
(указатель кадра, R11). Эти два также связаны. Этот
Макет Microsoft хорошо описывает вещи. Стек используется для хранения временных данных или локальных данных в вашей функции. Любые переменные в foo()
и bar()
хранятся здесь, в стеке или в доступных регистрах. fp
отслеживает переменные от функции к функции. Это окно кадра или изображения в стеке для этой функции. ABI определяет макет этого кадра. Обычно lr
и другие регистры сохраняются здесь за кулисами компилятором, а также предыдущим значением fp
. Это создает связанный список стековых фреймов, и если вы хотите, вы можете проследить его до main()
. Корнем является fp
, который указывает на один кадр стека (например, struct
) с одной переменной в struct
, являющейся предыдущей fp
. Вы можете идти по списку до финального fp
, который обычно NULL
.
Таким образом, sp
находится там, где находится стек, а fp
- там, где был стек, во многом как pc
и lr
. Каждый старый lr
(регистр связи) сохраняется в старом fp
(указатель кадра). sp
и fp
являются аспектом данных функций.
Ваша точка B является активными pc
и sp
. Точка A на самом деле является fp
и lr
; если вы не вызовете еще одну функцию, а затем компилятор может подготовиться к настройке fp
для указания на данные в B.
Ниже приведен пример сборщика ARM, который может продемонстрировать, как все это работает. Это будет отличаться в зависимости от того, как компилятор оптимизирует, но это должно дать представление,
; Prologue - setup
mov ip, sp ; get a copy of sp.
stmdb sp!, {fp, ip, lr, pc} ; Save the frame on the stack. See Addendum
sub fp, ip, #4 ; Set the new frame pointer.
...
; Maybe other functions called here.
; Older caller return lr
stored in stack frame.
bl baz
...
; Epilogue - return
ldm sp, {fp, sp, lr} ; restore stack, frame pointer and old link.
... ; maybe more stuff here.
bx lr ; return.
Вот как бы выглядел foo()
. Если вы не вызываете bar()
, то компилятор выполняет листовую оптимизацию и ему не нужно сохранять фрейм; нужен только bx lr
. Скорее всего, это может быть, почему вы запутались в веб-примерах. Это не всегда одно и то же.
Еда на вынос должна быть,
pc
иlr
связаны кодовыми регистрами. Один - "Где ты", другой - "Где ты был".sp
иfp
связаны локальными данными регистрами.
Один из них "Где локальные данные", другой - "Где последние локальные данные".- Совместная работа с передачей параметров parameter passing для создания механизма функций.
- Трудно описать общий случай, потому что мы хотим, чтобы компиляторы были максимально быстрыми, чтобы они использовали все возможные приемы.
Эти понятия являются общими для всех процессоров и скомпилированных языков, хотя детали могут различаться. Использование регистра ссылки, указателя кадра является частью пролога функции и эпилога, и если вы все поняли, вы знаете, как переполнение стека работает в ARM.
Смотрите также: Соглашение о вызовах ARM.
Статья стека MSDN ARM
Обзор APCS Кембриджского университета
Блог трассировки стека ARM
Ссылка Apple ABI
Базовая компоновка кадра:
- fp [-0] сохранил
pc
, где мы сохранили этот кадр. - fp [-1] сохранил
lr
, адрес возврата для этой функции. - fp [-2] предыдущий
sp
, до того как эта функция съест стек. - fp [-3] предыдущий
fp
, последний кадр стека. - много необязательных регистров...
ABI может использовать другие значения, но приведенные выше типичны для большинства установок.
Приложение: Это не ошибка в ассемблере; это нормально. Объяснение в вопросе о прологах, созданных ARM.