Все метки, за исключением меток, начинающихся с префикса .L
, попадают в таблицу символов, которую можно посмотреть командой objdump -t
. Метки могут быть как локальными, то есть предназначенными только для использования в пределах объектного файла, так и глобальными, - доступными извне. Чтобы сделать метку глобальной, необходимо использовать директиву .global
в исходном тексте на языке ассемблера. С точки зрения внутреннего представления, функции и метки - это одни и тоже.
Переход на метку осуществляется инструкцией b
, возможно, с каким-то суффиксом-условием. Для вызова функций используется инструкция bl
, которая выполняет следующие действия:
- Сохраняет в регистр
x30
значениеpc + 4
- Выполняет переход на метку, указанную в инструкции.
Регистр x30
при этом имеет специальное назначение - Link Register, в некоторых источниках его еще называют lr
, хотя компилятор gcc
явно это имя не поддерживает для архитектуры aarh64
. Смысл этого регистра в том, что он хранит адрес возврата из функции.
Набор регистров процессора существует только в единственном экземпляре, поэтому при вызове функций все регистры используются повторно, и это необходимо учитывать. Для определенных групп регистров, по соглашению, принятому в 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