Skip to content

Latest commit

 

History

History
 
 

aarch64-functions

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Стек и вызов функций

Функции и метки

Все метки, за исключением меток, начинающихся с префикса .L, попадают в таблицу символов, которую можно посмотреть командой objdump -t. Метки могут быть как локальными, то есть предназначенными только для использования в пределах объектного файла, так и глобальными, - доступными извне. Чтобы сделать метку глобальной, необходимо использовать директиву .global в исходном тексте на языке ассемблера. С точки зрения внутреннего представления, функции и метки - это одни и тоже.

Переход на метку осуществляется инструкцией b, возможно, с каким-то суффиксом-условием. Для вызова функций используется инструкция bl, которая выполняет следующие действия:

  1. Сохраняет в регистр x30 значение pc + 4
  2. Выполняет переход на метку, указанную в инструкции.

Регистр x30 при этом имеет специальное назначение - Link Register, в некоторых источниках его еще называют lr, хотя компилятор gcc явно это имя не поддерживает для архитектуры aarh64. Смысл этого регистра в том, что он хранит адрес возврата из функции.

Соглашение о вызовах функций в Linux/AArch64

Набор регистров процессора существует только в единственном экземпляре, поэтому при вызове функций все регистры используются повторно, и это необходимо учитывать. Для определенных групп регистров, по соглашению, принятому в Linux (и большинстве других систем), принято следующее функциональное назначение:

  • Регистры с x0 до x7 включительно используются для передачи аргументов в функцию. Кроме того, регистр x0 используется для вовзрата значения.
  • Регистр x8 используется в качестве указателя this для объектно-ориентированных языков программирования.
  • Регистры  c x9 по x15 используются как временные регистры.
  • Регистры x16 и x17 используются как временные регистры для вычисления адреса функции во время прыжка из секции .plt.
  • Регистр x18 используется как указатель на хранилище Thread-local переменных.
  • Регистры с x19 по x28 должны быть сохранены, если они используются функцией.
  • Регистр x29 используется как Frame Pointer, используемый для быстрого разворачиваения стека вызовов.
  • Регистр x30 - это Link Register, используемый инструкцией ret.
  • Регистр x31 (для gcc его имя sp) - это указатель на вершину стека.

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

Для регистров с x19 по x31 гарантируется, что их значения не изменятся в случае вызова функции. В случае, если какой-либо функции требуется использование этих регистров, то функция обязана восстановить их исходные значения перед вызовом инструкции ret.

Стек вызовов

Для хранения локальный значений и сохранения регистров с x19 по x30 может использоваться оперативная память в специальной области, которая называется стек. Память для стека имеет фиксированный размер (для Linux по умолчанию это обычно 8Мб), и она выделена перед началом выполнения программы.

Данные в стек помещаются сверху вниз, а указатель на нижнюю границу стека для каждой функции хранится в регистре sp. Кроме того, для архитектуры aarch64 (независимо от используемой операционной системы) требуется, чтобы стек был выровнен по границе в 16 байт, в противном случае обращение к стеку приведет к ошибке Bus Error.

Пример. Выделение памяти на стеке и сохранение регистра lr:

function:
    // выделяем память на стеке, просто перемещая указатель sp
    // обратите внимание на то, что изменять стек можно на число,
    // кратное 16, даже если требуется меньше памяти
    sub sp, sp, 16
    // сохраняем на стек регистр x30
    // можно сохранять как в самый низ стека [sp],
    // так и со смещением в 8 байт [sp, 8]
    str x30, [sp, 8]
    
    // .... какой-то код с использованием инструкции bl
    
    // перед выходом восстанавливаем из стека x30
    ldr x30, [sp, 8]
    // вовзращаем значение sp в исходное
    add sp, sp, 16
    // теперь можно выходить
    ret