Вложенные вызовы функций

Последнее обновление: 19.07.2023

Одна функция может вызывать другую функцию. Это несколько усложняет процесс управления вызовами функции. Например, мы имее следующую упрощенную конструкцию

.global _start 
_start: 
    BL outerfunc
    MOV X0, 5

outerfunc:
    BL innerfunc
    RET

innerfunc:
    RET

Здесь вызывается функция outerfunc, и, как и ожидается, инструкция BL копирует адрес следующей инструкции в регистр LR (он же регистр X30). Но в outerfunc вызывается другая функция - innerfunc. В этом случае инструкция BL опять же копирует адрес следующей инструкции (теперь адрес следующей инструкции в функции outerfunc) в регистр LR и тем самым затирает ранее сохраненный адрес. И таким образом, мы не сможем возвратиться из функции outerfunc. Соответственно напрашивается решение, что необходимо сохранять всю цепочку адресов возврата. И для этой цели можно использовать стек.

Если функция outerfunc вызывает другие функции, то она должна сохранить содержимое регистра LR в стек, а после выполнения вложенных функций загрузить обратно из стека сохраненный адрес. В общем случае это выглядит так:

.global _start 
_start: 
    BL outerfunc        // вызываем функцию outerfunc

    MOV X0, 5           // для теста устанавливаем код возврата - 5
    MOV X8, #93       // устанавливаем функцию Linux для выхода из программы
    SVC 0             // Вызываем функцию Linux

outerfunc:
    STR LR, [SP, #-16]!     // сохраняем в стеке текущий адрес из регистра LR
    BL innerfunc            // вызываем функцию innerfunc
    LDR LR, [SP], #16       // извлекаем из стека адрес в регистр LR
    RET
// вложенная функция
innerfunc: 
    RET

Причем, как и вообщем случае, проблема касается не только регистра LR, но всех остальных регистров, которые может изменить вложенная функции и которые используются во внешнем коде.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850